Compare commits
No commits in common. "lastFOSS" and "v5-develop" have entirely different histories.
lastFOSS
...
v5-develop
5303 changed files with 3170074 additions and 501789 deletions
3
.babelrc
Normal file
3
.babelrc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"plugins": ["@babel/plugin-proposal-class-properties"]
|
||||
}
|
||||
3
.bowerrc
3
.bowerrc
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"directory": "./resources/assets/bower"
|
||||
}
|
||||
5
.codacy.yml
Normal file
5
.codacy.yml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Codacy configuration file for Invoice Ninja
|
||||
|
||||
exclude_paths:
|
||||
- 'public/css/*'
|
||||
- 'public/js/*'
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
engines:
|
||||
csslint:
|
||||
enabled: true
|
||||
duplication:
|
||||
enabled: true
|
||||
config:
|
||||
languages:
|
||||
- ruby
|
||||
- javascript
|
||||
- python
|
||||
- php
|
||||
eslint:
|
||||
enabled: false
|
||||
fixme:
|
||||
enabled: true
|
||||
phpmd:
|
||||
enabled: true
|
||||
ratings:
|
||||
paths:
|
||||
- "**.css"
|
||||
- "**.inc"
|
||||
- "**.js"
|
||||
- "**.jsx"
|
||||
- "**.module"
|
||||
- "**.php"
|
||||
- "**.py"
|
||||
- "**.rb"
|
||||
exclude_paths:
|
||||
- "bootstrap/cache"
|
||||
- "resources/"
|
||||
- "storage/"
|
||||
- "tests/"
|
||||
- "public/"
|
||||
- "**.md"
|
||||
- "**.min.js"
|
||||
- "**.min.php"
|
||||
- "**.min.css"
|
||||
15
.editorconfig
Normal file
15
.editorconfig
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
23
.env.ci
Normal file
23
.env.ci
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
APP_NAME="Invoice Ninja"
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://ninja.test
|
||||
MULTI_DB_ENABLED=false
|
||||
# database
|
||||
DB_CONNECTION=mysql
|
||||
DB_DATABASE1=ninja
|
||||
DB_USERNAME1=root
|
||||
DB_PASSWORD1=ninja
|
||||
DB_HOST1=127.0.0.1
|
||||
DB_PORT1=32768
|
||||
DB_PORT=32768
|
||||
DB_DATABASE=ninja
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=ninja
|
||||
DB_HOST=127.0.0.1
|
||||
NINJA_ENVIRONMENT=hosted
|
||||
COMPOSER_AUTH='{"github-oauth": {"github.com": "${{ secrets.GITHUB_TOKEN }}"}}'
|
||||
TRAVIS=true
|
||||
API_SECRET=superdoopersecrethere
|
||||
PHANTOMJS_PDF_GENERATION=false
|
||||
27
.env.dusk.example
Normal file
27
.env.dusk.example
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
APP_ENV=local
|
||||
APP_DEBUG=true
|
||||
APP_LOCALE=en
|
||||
APP_URL=http://127.0.0.1:8000
|
||||
APP_KEY=s7epnjtomsdond5zgfqgaqmwhhcjct02
|
||||
REQUIRE_HTTPS=false
|
||||
NINJA_ENVIRONMENT=development
|
||||
|
||||
DB_TYPE=mysql
|
||||
DB_STRICT=false
|
||||
DB_HOST=localhost
|
||||
DB_USERNAME=ninja
|
||||
DB_PASSWORD=ninja
|
||||
|
||||
DB_CONNECTION=db-ninja-01
|
||||
DB_DATABASE1=db-ninja-01
|
||||
DB_DATABASE2=db-ninja-02
|
||||
MAIL_MAILER=log
|
||||
MAIL_PORT=587
|
||||
MAIL_ENCRYPTION=tls
|
||||
MAIL_HOST=
|
||||
MAIL_USERNAME=
|
||||
MAIL_FROM_NAME=
|
||||
MAIL_FROM_ADDRESS=
|
||||
MAIL_PASSWORD=
|
||||
MAILGUN_DOMAIN=
|
||||
MAILGUN_SECRET=
|
||||
131
.env.example
131
.env.example
|
|
@ -1,107 +1,70 @@
|
|||
APP_NAME="Invoice Ninja"
|
||||
APP_ENV=production
|
||||
APP_KEY=base64:RR++yx2rJ9kdxbdh3+AmbHLDQu+Q76i++co9Y8ybbno=
|
||||
APP_DEBUG=false
|
||||
APP_URL=http://www.ninja.test
|
||||
APP_KEY=SomeRandomStringSomeRandomString
|
||||
APP_CIPHER=AES-256-CBC
|
||||
APP_LOCALE=en
|
||||
|
||||
DB_TYPE=mysql
|
||||
DB_STRICT=false
|
||||
APP_URL=http://localhost
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
MULTI_DB_ENABLED=false
|
||||
|
||||
DB_HOST=localhost
|
||||
DB_DATABASE=ninja
|
||||
DB_USERNAME=ninja
|
||||
DB_PASSWORD=ninja
|
||||
DB_PORT=3306
|
||||
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_PORT=587
|
||||
MAIL_ENCRYPTION=tls
|
||||
MAIL_HOST
|
||||
MAIL_USERNAME
|
||||
MAIL_FROM_ADDRESS
|
||||
MAIL_FROM_NAME
|
||||
MAIL_PASSWORD
|
||||
DEMO_MODE=false
|
||||
|
||||
MAILGUN_DOMAIN=
|
||||
MAILGUN_SECRET=
|
||||
BROADCAST_DRIVER=log
|
||||
LOG_CHANNEL=stack
|
||||
CACHE_DRIVER=file
|
||||
QUEUE_CONNECTION=sync
|
||||
SESSION_DRIVER=file
|
||||
SESSION_LIFETIME=120
|
||||
|
||||
#POSTMARK_API_TOKEN=
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
PHANTOMJS_CLOUD_KEY='a-demo-key-with-low-quota-per-ip-address'
|
||||
#PHANTOMJS_BIN_PATH=/usr/local/bin/phantomjs
|
||||
MAIL_MAILER=smtp
|
||||
MAIL_HOST=smtp.mailtrap.io
|
||||
MAIL_PORT=2525
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
MAIL_FROM_ADDRESS='user@example.com'
|
||||
MAIL_FROM_NAME='Self Hosted User'
|
||||
|
||||
LOG=single
|
||||
POSTMARK_API_TOKEN=
|
||||
REQUIRE_HTTPS=false
|
||||
API_SECRET=password
|
||||
|
||||
#TRUSTED_PROXIES=
|
||||
GOOGLE_MAPS_API_KEY=
|
||||
ERROR_EMAIL=
|
||||
TRUSTED_PROXIES=
|
||||
|
||||
#SESSION_DRIVER=
|
||||
#SESSION_DOMAIN=
|
||||
#SESSION_ENCRYPT=
|
||||
#SESSION_SECURE=
|
||||
NINJA_ENVIRONMENT=selfhost
|
||||
|
||||
#CACHE_DRIVER=
|
||||
#CACHE_HOST=
|
||||
#REDIS_HOST=
|
||||
#CACHE_PORT1=
|
||||
#CACHE_PORT2=
|
||||
#options - snappdf / phantom / hosted_ninja
|
||||
PDF_GENERATOR=hosted_ninja
|
||||
|
||||
#GOOGLE_CLIENT_ID=
|
||||
#GOOGLE_CLIENT_SECRET=
|
||||
#GOOGLE_OAUTH_REDIRECT=http://ninja.test/auth/google
|
||||
PHANTOMJS_KEY='a-demo-key-with-low-quota-per-ip-address'
|
||||
PHANTOMJS_SECRET=secret
|
||||
|
||||
GOOGLE_MAPS_ENABLED=true
|
||||
#GOOGLE_MAPS_API_KEY=
|
||||
UPDATE_SECRET=secret
|
||||
|
||||
# Create a cookie to stay logged in
|
||||
#REMEMBER_ME_ENABLED=true
|
||||
DELETE_PDF_DAYS=60
|
||||
DELETE_BACKUP_DAYS=60
|
||||
|
||||
# Immediately expire cookie on the browser closing
|
||||
#SESSION_EXPIRE_ON_CLOSE=false
|
||||
COMPOSER_AUTH='{"github-oauth": {"github.com": "${{ secrets.GITHUB_TOKEN }}"}}'
|
||||
|
||||
# The app automatically logs the user out after this number of seconds
|
||||
#AUTO_LOGOUT_SECONDS=28800
|
||||
GOOGLE_PLAY_PACKAGE_NAME=
|
||||
APPSTORE_PASSWORD=
|
||||
|
||||
#S3_KEY=
|
||||
#S3_SECRET=
|
||||
#S3_REGION=
|
||||
#S3_BUCKET=
|
||||
MICROSOFT_CLIENT_ID=
|
||||
MICROSOFT_CLIENT_SECRET=
|
||||
MICROSOFT_REDIRECT_URI=
|
||||
|
||||
#RACKSPACE_USERNAME=
|
||||
#RACKSPACE_KEY=
|
||||
#RACKSPACE_CONTAINER=
|
||||
#RACKSPACE_REGION=
|
||||
|
||||
#RACKSPACE_TEMP_URL_SECRET=
|
||||
|
||||
# If this is set to anything, the URL secret will be set the next
|
||||
# time a file is downloaded through the client portal.
|
||||
# Only set this temporarily, as it slows things down.
|
||||
#RACKSPACE_TEMP_URL_SECRET_SET=
|
||||
|
||||
#DOCUMENT_FILESYSTEM=
|
||||
|
||||
#MAX_DOCUMENT_SIZE # KB
|
||||
#MAX_EMAIL_DOCUMENTS_SIZE # Total KB
|
||||
#MAX_ZIP_DOCUMENTS_SIZE # Total KB (uncompressed)
|
||||
#DOCUMENT_PREVIEW_SIZE # Pixels
|
||||
|
||||
WEPAY_CLIENT_ID=
|
||||
WEPAY_CLIENT_SECRET=
|
||||
WEPAY_ENVIRONMENT=production # production or stage
|
||||
WEPAY_AUTO_UPDATE=true # Requires permission from WePay
|
||||
WEPAY_FEE_PAYER=payee
|
||||
WEPAY_APP_FEE_CC_MULTIPLIER=0
|
||||
WEPAY_APP_FEE_ACH_MULTIPLIER=0
|
||||
WEPAY_APP_FEE_FIXED=0
|
||||
WEPAY_THEME='{"name":"Invoice Ninja","primary_color":"0b4d78","secondary_color":"0b4d78","background_color":"f8f8f8","button_color":"33b753"}' # See https://www.wepay.com/developer/reference/structures#theme
|
||||
|
||||
|
||||
BLUEVINE_PARTNER_UNIQUE_ID=
|
||||
BLUEVINE_PARTNER_TOKEN=
|
||||
|
||||
CLOUDFLARE_DNS_ENABLED=false
|
||||
CLOUDFLARE_API_KEY=
|
||||
CLOUDFLARE_EMAIL=
|
||||
CLOUDFLARE_TARGET_IP_ADDRESS=
|
||||
CLOUDFLARE_ZONE_IDS={}
|
||||
APPLE_CLIENT_ID=
|
||||
APPLE_CLIENT_SECRET=
|
||||
APPLE_REDIRECT_URI=
|
||||
|
|
|
|||
19
.env.travis
19
.env.travis
|
|
@ -1,25 +1,24 @@
|
|||
APP_ENV=development
|
||||
APP_ENV=local
|
||||
APP_DEBUG=false
|
||||
APP_URL=http://ninja.test
|
||||
APP_URL=http://127.0.0.1:8000
|
||||
APP_KEY=SomeRandomStringSomeRandomString
|
||||
APP_CIPHER=AES-256-CBC
|
||||
APP_LOCALE=en
|
||||
|
||||
MULTI_DB_ENABLED=true
|
||||
MULTI_DB_CACHE_ENABLED=true
|
||||
|
||||
DB_TYPE=db-ninja-1
|
||||
DB_STRICT=false
|
||||
DB_HOST=localhost
|
||||
DB_USERNAME=ninja
|
||||
DB_PASSWORD=ninja
|
||||
|
||||
DB_DATABASE0=ninja0
|
||||
DB_DATABASE1=ninja
|
||||
DB_DATABASE2=ninja2
|
||||
DB_CONNECTION=db-ninja-01
|
||||
DB_DATABASE1=ninja01
|
||||
DB_DATABASE2=ninja02
|
||||
|
||||
MAIL_DRIVER=log
|
||||
MAIL_MAILER=log
|
||||
TRAVIS=true
|
||||
API_SECRET=password
|
||||
TEST_USERNAME=user@example.com
|
||||
TEST_PERMISSIONS_USERNAME=permissions@example.com
|
||||
|
||||
MULTI_DB_ENABLED=true
|
||||
NINJA_ENVIRONMENT=development
|
||||
|
|
|
|||
9
.gitattributes
vendored
9
.gitattributes
vendored
|
|
@ -1,8 +1,5 @@
|
|||
* text=auto
|
||||
*.css linguist-vendored
|
||||
*.less linguist-vendored
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
.codeclimate.yml export-ignore
|
||||
.travis.yml export-ignore
|
||||
.styleci.yml export-ignore
|
||||
*.scss linguist-vendored
|
||||
*.js linguist-vendored
|
||||
CHANGELOG.md export-ignore
|
||||
|
|
|
|||
47
.github/ISSUE_TEMPLATE/bug_report.md
vendored
47
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
|
@ -7,35 +7,36 @@ assignees: ''
|
|||
|
||||
---
|
||||
|
||||
**What version of Invoice Ninja are you running? ie v4.5.25 / v5.0.30**
|
||||
<!-- Before posting please check our "Troubleshooting" category in the docs:
|
||||
https://invoiceninja.github.io/docs/self-host-troubleshooting/ -->
|
||||
|
||||
**What environment are you running?**
|
||||
Docker
|
||||
Shared Hosting
|
||||
ZIP
|
||||
Other
|
||||
## Setup
|
||||
- Version: <!-- i.e. v4.5.25 / v5.0.30 -->
|
||||
- Environment: <!-- Docker/Shared Hosting/ZIP/Other -->
|
||||
|
||||
**Have you checked log files (storage/logs/) Please provide redacted output**
|
||||
## Checklist
|
||||
- Can you replicate the issue on our v5 demo site https://demo.invoiceninja.com or https://react.invoicing.co/demo?
|
||||
- Have you searched existing issues?
|
||||
- Have you reported this to Slack/forum before posting?
|
||||
- Have you inspected the logs in storage/logs/laravel.log for any errors?
|
||||
|
||||
**Have you searched existing issues?**
|
||||
## Describe the bug
|
||||
<!-- A clear and concise description of the bug. -->
|
||||
|
||||
**Have you reported this to Slack/forum before posting?**
|
||||
### Steps To Reproduce
|
||||
<!-- Please list the steps to reproduce the issue. -->
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
### Expected Behavior
|
||||
<!-- A clear and concise description of what you expected to happen. -->
|
||||
|
||||
**Steps To Reproduce**
|
||||
Please list the steps to reproduce the issue
|
||||
## Additional context
|
||||
<!-- Add any other context about the problem here. -->
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
### Screenshots
|
||||
<!-- If applicable, add screenshots to help explain your problem. -->
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
### Logs
|
||||
<!-- Please check the log files (storage/logs/) and provide redacted output -->
|
||||
```
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
Note: Before posting don't forget to check our "Troubleshooting" category in the [docs](https://invoiceninja.github.io/docs/self-host-troubleshooting/) (https://invoiceninja.github.io/docs/self-host-troubleshooting/).
|
||||
|
||||
**(v5) Can you replicate the issue on our demo site? https://demo.invoiceninja.com**
|
||||
```
|
||||
133
.github/workflows/phpunit.yml
vendored
Normal file
133
.github/workflows/phpunit.yml
vendored
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
on:
|
||||
push:
|
||||
branches:
|
||||
- v5-develop
|
||||
- v5-stable
|
||||
pull_request:
|
||||
branches:
|
||||
- v5-develop
|
||||
|
||||
name: phpunit
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
strategy:
|
||||
matrix:
|
||||
operating-system: ['ubuntu-20.04', 'ubuntu-22.04']
|
||||
php-versions: ['8.1','8.2']
|
||||
phpunit-versions: ['latest']
|
||||
ci_node_total: [ 8 ]
|
||||
ci_node_index: [ 0, 1, 2, 3, 4, 5, 6, 7]
|
||||
laravel: [9.*]
|
||||
dependency-version: [prefer-stable]
|
||||
|
||||
env:
|
||||
DB_DATABASE1: ninja
|
||||
DB_USERNAME1: root
|
||||
DB_PASSWORD1: ninja
|
||||
DB_HOST1: '127.0.0.1'
|
||||
DB_DATABASE: ninja
|
||||
DB_USERNAME: root
|
||||
DB_PASSWORD: ninja
|
||||
DB_HOST: '127.0.0.1'
|
||||
REDIS_PORT: 6379
|
||||
BROADCAST_DRIVER: log
|
||||
CACHE_DRIVER: redis
|
||||
QUEUE_CONNECTION: redis
|
||||
SESSION_DRIVER: redis
|
||||
NINJA_ENVIRONMENT: hosted
|
||||
MULTI_DB_ENABLED: false
|
||||
NINJA_LICENSE: ${{ secrets.ninja_license }}
|
||||
TRAVIS: true
|
||||
MAIL_MAILER: log
|
||||
|
||||
services:
|
||||
mariadb:
|
||||
image: mariadb:latest
|
||||
ports:
|
||||
- 32768:3306
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||
MYSQL_USER: ninja
|
||||
MYSQL_PASSWORD: ninja
|
||||
MYSQL_DATABASE: ninja
|
||||
MYSQL_ROOT_PASSWORD: ninja
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
||||
redis:
|
||||
image: redis
|
||||
ports:
|
||||
- 6379/tcp
|
||||
options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
||||
|
||||
steps:
|
||||
- name: Add hosts to /etc/hosts
|
||||
run: |
|
||||
sudo echo "127.0.0.1 ninja.test" | sudo tee -a /etc/hosts
|
||||
|
||||
- name: Start MariaDB service
|
||||
run: |
|
||||
sudo systemctl start mysql.service
|
||||
- name: Verify MariaDB connection
|
||||
env:
|
||||
DB_PORT: ${{ job.services.mariadb.ports[3306] }}
|
||||
DB_PORT1: ${{ job.services.mariadb.ports[3306] }}
|
||||
|
||||
run: |
|
||||
while ! mysqladmin ping -h"127.0.0.1" -P"$DB_PORT" --silent; do
|
||||
sleep 1
|
||||
done
|
||||
- name: Setup PHP shivammathur/setup-php@v2
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
extensions: mysql, mysqlnd, sqlite3, bcmath, gmp, gd, curl, zip, openssl, mbstring, xml, redis
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
ref: v5-develop
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Copy .env
|
||||
run: |
|
||||
cp .env.ci .env
|
||||
|
||||
- name: Get Composer Cache Directory
|
||||
id: composer-cache
|
||||
run: |
|
||||
echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.php }}-composer-
|
||||
|
||||
- name: Install composer dependencies
|
||||
run: |
|
||||
composer config -g github-oauth.github.com ${{ secrets.GITHUB_TOKEN }}
|
||||
composer install
|
||||
|
||||
- name: Prepare Laravel Application
|
||||
env:
|
||||
REDIS_PORT: ${{ job.services.redis.ports['6379'] }}
|
||||
run: |
|
||||
php artisan key:generate
|
||||
php artisan optimize
|
||||
php artisan cache:clear
|
||||
php artisan config:cache
|
||||
php artisan ninja:post-update
|
||||
|
||||
- name: Migrate Database
|
||||
run: |
|
||||
php artisan migrate:fresh --seed --force && php artisan db:seed --force
|
||||
|
||||
- name: Run Testsuite
|
||||
run: |
|
||||
cat .env
|
||||
vendor/bin/snappdf download
|
||||
tests/ci
|
||||
env:
|
||||
DB_PORT: ${{ job.services.mysql.ports[3306] }}
|
||||
PHP_CS_FIXER_IGNORE_ENV: true
|
||||
CI_NODE_TOTAL: ${{ matrix.ci_node_total }}
|
||||
CI_NODE_INDEX: ${{ matrix.ci_node_index }}
|
||||
77
.github/workflows/release.yml
vendored
Normal file
77
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
on:
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
name: Upload Release Asset
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Upload Release Asset
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 8.1
|
||||
extensions: mysql, mysqlnd, sqlite3, bcmath, gd, curl, zip, openssl, mbstring, xml
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v1
|
||||
with:
|
||||
ref: v5-develop
|
||||
|
||||
- name: Copy .env file
|
||||
run: |
|
||||
cp .env.example .env
|
||||
|
||||
- name: Install composer dependencies
|
||||
run: |
|
||||
composer config -g github-oauth.github.com ${{ secrets.GITHUB_TOKEN }}
|
||||
composer install --no-dev
|
||||
|
||||
- name: Prepare Laravel Application
|
||||
run: |
|
||||
cp .env.example .env
|
||||
php artisan key:generate
|
||||
php artisan optimize
|
||||
php artisan storage:link
|
||||
sudo php artisan cache:clear
|
||||
sudo find ./vendor/bin/ -type f -exec chmod +x {} \;
|
||||
sudo find ./ -type d -exec chmod 755 {} \;
|
||||
|
||||
- name: Prepare React FrontEnd
|
||||
run: |
|
||||
git clone https://${{secrets.commit_secret}}@github.com/invoiceninja/ui.git
|
||||
cd ui
|
||||
git checkout main
|
||||
npm i
|
||||
npm run build
|
||||
cp -r dist/react/* ../public/react
|
||||
cd ..
|
||||
rm -rf ui
|
||||
php artisan ninja:react
|
||||
|
||||
- name: Prepare JS/CSS assets
|
||||
run: |
|
||||
npm i
|
||||
npm run production
|
||||
|
||||
- name: Cleanup Builds
|
||||
run: |
|
||||
sudo rm -rf bootstrap/cache/*
|
||||
sudo rm -rf node_modules
|
||||
sudo rm -rf .git
|
||||
sudo rm .env
|
||||
|
||||
- name: Build project
|
||||
run: |
|
||||
zip -r ./invoiceninja.zip .* -x "../*"
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
files: |
|
||||
invoiceninja.zip
|
||||
80
.gitignore
vendored
80
.gitignore
vendored
|
|
@ -1,46 +1,40 @@
|
|||
/config/staging
|
||||
/config/development
|
||||
/config/production
|
||||
/config/fortrabbit
|
||||
/config/ubuntu
|
||||
/config/packages/anahkiasen/rocketeer/
|
||||
/public/logo
|
||||
/public/build
|
||||
/public/packages
|
||||
/public/vendor
|
||||
/resources/assets/bower
|
||||
/storage/*.key
|
||||
/storage/documents
|
||||
/storage/import
|
||||
/storage/migrations
|
||||
/bootstrap/compiled.php
|
||||
/bootstrap/environment.php
|
||||
/vendor
|
||||
/node_modules
|
||||
/.DS_Store
|
||||
Thumbs.db
|
||||
/.env
|
||||
/.env.development.php
|
||||
/.env.php
|
||||
/docs/_build
|
||||
/error_log
|
||||
/auth.json
|
||||
/public/error_log
|
||||
/Modules
|
||||
|
||||
/ninja.sublime-project
|
||||
/ninja.sublime-workspace
|
||||
/.phpstorm.meta.php
|
||||
/_ide_helper.php
|
||||
/public/hot
|
||||
/public/storage
|
||||
/public/react
|
||||
/storage/*.key
|
||||
/storage/debugbar
|
||||
/storage/*
|
||||
/tests/bootstrap/
|
||||
/vendor
|
||||
/app/Console/Commands/vendor/
|
||||
/.idea
|
||||
/.project
|
||||
tests/_output/
|
||||
tests/_bootstrap.php
|
||||
tests/_support/_generated/
|
||||
|
||||
# composer stuff
|
||||
/c3.php
|
||||
|
||||
/.vscode
|
||||
/.vagrant
|
||||
/tests/_output
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
local_version.txt
|
||||
.env
|
||||
.phpunit.result.cache
|
||||
_ide_helper.php
|
||||
storage/version.txt
|
||||
storage/framework/.DS_Store
|
||||
|
||||
/resources/assets/bower
|
||||
/public/logo
|
||||
.env.dusk.local
|
||||
.env.cypress
|
||||
/public/vendors/*
|
||||
*.log
|
||||
|
||||
# Ignore local migrations
|
||||
storage/migrations
|
||||
nbproject
|
||||
|
||||
.php_cs.cache
|
||||
public/test.pdf
|
||||
public/storage/test.pdf
|
||||
/Modules
|
||||
_ide_helper_models.php
|
||||
_ide_helper.php
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteRule "^.env" - [F,L]
|
||||
RewriteRule "^storage" - [F,L]
|
||||
# RewriteRule "^storage" - [F,L]
|
||||
RewriteRule ^(.well-known)($|/) - [L]
|
||||
|
||||
# https://coderwall.com/p/erbaig/laravel-s-htaccess-to-remove-public-from-url
|
||||
# RewriteRule ^(.*)$ public/$1 [L]
|
||||
RewriteRule ^(.*)$ public/$1 [L]
|
||||
</IfModule>
|
||||
|
||||
# https://github.com/h5bp/server-configs-apache/blob/master/dist/.htaccess
|
||||
|
|
|
|||
98
.php_cs.dist
98
.php_cs.dist
|
|
@ -1,98 +0,0 @@
|
|||
<?php
|
||||
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->notPath('bootstrap/cache')
|
||||
->notPath('storage')
|
||||
->notPath('vendor')
|
||||
->in(__DIR__)
|
||||
->name('*.php')
|
||||
->notName('*.blade.php')
|
||||
->ignoreDotFiles(true)
|
||||
->ignoreVCS(true);
|
||||
|
||||
return PhpCsFixer\Config::create()
|
||||
->setRules([
|
||||
'@PSR2' => true,
|
||||
'binary_operator_spaces' => true,
|
||||
'blank_line_after_namespace' => true,
|
||||
'blank_line_after_opening_tag' => true,
|
||||
'blank_line_before_return' => true,
|
||||
'braces' => true,
|
||||
'cast_spaces' => true,
|
||||
'class_definition' => true,
|
||||
'elseif' => true,
|
||||
'encoding' => true,
|
||||
'full_opening_tag' => true,
|
||||
'function_declaration' => true,
|
||||
'function_typehint_space' => true,
|
||||
'hash_to_slash_comment' => true,
|
||||
'heredoc_to_nowdoc' => true,
|
||||
'include' => true,
|
||||
'linebreak_after_opening_tag' => true,
|
||||
'lowercase_cast' => true,
|
||||
'lowercase_constants' => true,
|
||||
'lowercase_keywords' => true,
|
||||
'method_argument_space' => true,
|
||||
'method_separation' => true,
|
||||
'native_function_casing' => true,
|
||||
'new_with_braces' => true,
|
||||
'no_alias_functions' => true,
|
||||
'no_blank_lines_after_class_opening' => true,
|
||||
'no_blank_lines_after_phpdoc' => true,
|
||||
'no_closing_tag' => true,
|
||||
'no_empty_phpdoc' => true,
|
||||
'no_extra_consecutive_blank_lines' => true,
|
||||
'no_leading_import_slash' => true,
|
||||
'no_leading_namespace_whitespace' => true,
|
||||
'no_multiline_whitespace_around_double_arrow' => true,
|
||||
'no_multiline_whitespace_before_semicolons' => true,
|
||||
'no_short_bool_cast' => true,
|
||||
'no_singleline_whitespace_before_semicolons' => true,
|
||||
'no_spaces_after_function_name' => true,
|
||||
'no_spaces_inside_parenthesis' => true,
|
||||
'no_trailing_comma_in_list_call' => true,
|
||||
'no_trailing_comma_in_singleline_array' => true,
|
||||
'no_trailing_whitespace' => true,
|
||||
'no_trailing_whitespace_in_comment' => true,
|
||||
'no_unneeded_control_parentheses' => true,
|
||||
'no_unreachable_default_argument_value' => true,
|
||||
'no_unused_imports' => true,
|
||||
'no_useless_return' => true,
|
||||
'no_whitespace_before_comma_in_array' => true,
|
||||
'not_operator_with_successor_space' => true,
|
||||
'object_operator_without_whitespace' => true,
|
||||
'ordered_imports' => true,
|
||||
'phpdoc_add_missing_param_annotation' => true,
|
||||
'phpdoc_align' => true,
|
||||
'phpdoc_indent' => true,
|
||||
'phpdoc_inline_tag' => true,
|
||||
'phpdoc_no_access' => true,
|
||||
'phpdoc_no_package' => true,
|
||||
'phpdoc_order' => true,
|
||||
'phpdoc_scalar' => true,
|
||||
'phpdoc_separation' => true,
|
||||
'phpdoc_summary' => true,
|
||||
'phpdoc_to_comment' => true,
|
||||
'phpdoc_trim' => true,
|
||||
'phpdoc_types' => true,
|
||||
'phpdoc_var_without_name' => true,
|
||||
'self_accessor' => true,
|
||||
'short_scalar_cast' => true,
|
||||
'single_blank_line_at_eof' => true,
|
||||
'single_blank_line_before_namespace' => true,
|
||||
'single_import_per_statement' => true,
|
||||
'single_line_after_imports' => true,
|
||||
'single_quote' => true,
|
||||
'space_after_semicolon' => true,
|
||||
'standardize_not_equals' => true,
|
||||
'switch_case_semicolon_to_colon' => true,
|
||||
'switch_case_space' => true,
|
||||
'ternary_operator_spaces' => true,
|
||||
'trailing_comma_in_multiline_array' => true,
|
||||
'trim_array_spaces' => true,
|
||||
'unary_operator_spaces' => true,
|
||||
'visibility_required' => true,
|
||||
'whitespace_after_comma_in_array' => true,
|
||||
'array_syntax' => array('syntax' => 'short'),
|
||||
])
|
||||
->setFinder($finder);
|
||||
6
.prettierrc
Normal file
6
.prettierrc
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"arrowParens": "always"
|
||||
}
|
||||
18
.styleci.yml
18
.styleci.yml
|
|
@ -1,18 +0,0 @@
|
|||
preset: laravel
|
||||
|
||||
risky: false
|
||||
|
||||
enabled:
|
||||
- no_useless_else
|
||||
- phpdoc_align
|
||||
- phpdoc_no_empty_return
|
||||
- phpdoc_order
|
||||
- phpdoc_separation
|
||||
|
||||
finder:
|
||||
exclude:
|
||||
- "resources"
|
||||
- "storage"
|
||||
- "tests"
|
||||
not-path:
|
||||
- "bootstrap/cache"
|
||||
138
.travis.yml
138
.travis.yml
|
|
@ -1,138 +0,0 @@
|
|||
language: php
|
||||
|
||||
services:
|
||||
- mysql
|
||||
|
||||
sudo: true
|
||||
|
||||
# Prevent tests from taking more than 50 minutes
|
||||
group: deprecated-2017Q4
|
||||
|
||||
php:
|
||||
- 7.2
|
||||
- 7.3
|
||||
|
||||
addons:
|
||||
hosts:
|
||||
- www.ninja.test
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- vendor
|
||||
- $HOME/.composer/cache
|
||||
|
||||
env:
|
||||
global:
|
||||
- COMPOSER_DISCARD_CHANGES=true
|
||||
- COMPOSER_NO_INTERACTION=1
|
||||
- COMPOSER_DISABLE_XDEBUG_WARN=1
|
||||
|
||||
before_install:
|
||||
# set GitHub token and update composer
|
||||
- if [ -n "$GH_TOKEN" ]; then composer config github-oauth.github.com ${GH_TOKEN}; fi;
|
||||
- composer self-update 1.10.19 && composer -V
|
||||
# - export USE_ZEND_ALLOC=0
|
||||
- rvm use 1.9.3 --install --fuzzy
|
||||
|
||||
install:
|
||||
# install Composer dependencies
|
||||
# - rm composer.lock
|
||||
# these providers require referencing git commit's which cause Travis to fail
|
||||
# - sed -i '/mollie/d' composer.json
|
||||
# - sed -i '/2checkout/d' composer.json
|
||||
- travis_retry composer install --prefer-dist;
|
||||
|
||||
before_script:
|
||||
# prevent MySQL went away error
|
||||
- mysql -u root -e 'SET @@GLOBAL.wait_timeout=28800;'
|
||||
# copy configuration files
|
||||
- cp .env.travis .env
|
||||
- cp tests/_bootstrap.php.default tests/_bootstrap.php
|
||||
- php artisan key:generate --no-interaction
|
||||
- sed -i '$a NINJA_DEV=true' .env
|
||||
# create the database and user
|
||||
- mysql -u root -e "create database IF NOT EXISTS ninja0;"
|
||||
- mysql -u root -e "create database IF NOT EXISTS ninja;"
|
||||
- mysql -u root -e "create database IF NOT EXISTS ninja2;"
|
||||
- mysql -u root -e "GRANT ALL PRIVILEGES ON ninja0.* To 'ninja'@'localhost' IDENTIFIED BY 'ninja'; FLUSH PRIVILEGES;"
|
||||
- mysql -u root -e "GRANT ALL PRIVILEGES ON ninja.* To 'ninja'@'localhost' IDENTIFIED BY 'ninja'; FLUSH PRIVILEGES;"
|
||||
- mysql -u root -e "GRANT ALL PRIVILEGES ON ninja2.* To 'ninja'@'localhost' IDENTIFIED BY 'ninja'; FLUSH PRIVILEGES;"
|
||||
# migrate and seed the database
|
||||
- php artisan migrate --database=db-ninja-0 --seed --no-interaction
|
||||
- php artisan migrate --database=db-ninja-1 --seed --no-interaction
|
||||
- php artisan migrate --database=db-ninja-2 --seed --no-interaction
|
||||
# Start webserver on ninja.test:8000
|
||||
- php artisan serve --host=www.ninja.test --port=8000 & # '&' allows to run in background
|
||||
# Start PhantomJS
|
||||
- phantomjs --webdriver=4444 & # '&' allows to run in background
|
||||
# Give it some time to start
|
||||
- sleep 5
|
||||
# Make sure the app is up-to-date
|
||||
- curl -L http://www.ninja.test:8000/update
|
||||
- php artisan ninja:create-test-data 4 true
|
||||
- php artisan db:seed --no-interaction --class=UserTableSeeder # development seed
|
||||
- sed -i 's/DB_TYPE=db-ninja-1/DB_TYPE=db-ninja-2/g' .env
|
||||
- sed -i 's/user@example.com/user2@example.com/g' .env
|
||||
- sed -i 's/permissions@example.com/permissions2@example.com/g' .env
|
||||
- php artisan db:seed --no-interaction --class=UserTableSeeder # development seed
|
||||
|
||||
script:
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance APICest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance TaxRatesCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance CheckBalanceCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance ClientCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance ExpenseCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance CreditCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance InvoiceCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance QuoteCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance InvoiceDesignCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance OnlinePaymentCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance PaymentCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance TaskCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance AllPagesCept.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug functional PermissionsCest.php
|
||||
|
||||
#- sed -i 's/NINJA_DEV=true/NINJA_PROD=true/g' .env
|
||||
#- php ./vendor/codeception/codeception/codecept run --debug run acceptance GoProCest.php
|
||||
#- php ./vendor/codeception/codeception/codecept run --debug acceptance GatewayFeesCest.php
|
||||
#- php ./vendor/codeception/codeception/codecept run --debug acceptance DiscountCest.php
|
||||
|
||||
after_script:
|
||||
- php artisan ninja:check-data --no-interaction --database='db-ninja-1'
|
||||
- php artisan ninja:check-data --no-interaction --database='db-ninja-2'
|
||||
- php artisan ninja:init-lookup --validate=true --database='db-ninja-1'
|
||||
- php artisan ninja:init-lookup --validate=true --database='db-ninja-2'
|
||||
- cat storage/logs/laravel-error.log
|
||||
- cat storage/logs/laravel-info.log
|
||||
- cat .env
|
||||
- mysql -u root -e 'select * from lookup_companies;' ninja0
|
||||
- mysql -u root -e 'select * from lookup_accounts;' ninja0
|
||||
- mysql -u root -e 'select * from lookup_contacts;' ninja0
|
||||
- mysql -u root -e 'select * from lookup_invitations;' ninja0
|
||||
- mysql -u root -e 'select * from accounts;' ninja
|
||||
- mysql -u root -e 'select * from users;' ninja
|
||||
- mysql -u root -e 'select * from lookup_users;' ninja
|
||||
- mysql -u root -e 'select * from account_gateways;' ninja
|
||||
- mysql -u root -e 'select * from clients;' ninja
|
||||
- mysql -u root -e 'select * from contacts;' ninja
|
||||
- mysql -u root -e 'select id, public_id, account_id, invoice_number, amount, balance from invoices;' ninja
|
||||
- mysql -u root -e 'select * from invoice_items;' ninja
|
||||
- mysql -u root -e 'select * from invitations;' ninja
|
||||
- mysql -u root -e 'select id, public_id, account_id, invoice_id, amount, transaction_reference from payments;' ninja
|
||||
- mysql -u root -e 'select * from credits;' ninja
|
||||
- mysql -u root -e 'select * from expenses;' ninja
|
||||
- mysql -u root -e 'select * from accounts;' ninja
|
||||
- mysql -u root -e 'select * from fonts;' ninja
|
||||
- mysql -u root -e 'select * from banks;' ninja
|
||||
- mysql -u root -e 'select * from account_gateway_tokens;' ninja
|
||||
- mysql -u root -e 'select * from payment_methods;' ninja
|
||||
- FILES=$(find tests/_output -type f -name '*.png' | sort -nr)
|
||||
- for i in $FILES; do echo $i; base64 "$i"; break; done
|
||||
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
||||
on_failure: change
|
||||
slack:
|
||||
invoiceninja: SLraaKBDvjeRuRtY9o3Yvp1b
|
||||
156
CONTRIBUTING.md
156
CONTRIBUTING.md
|
|
@ -1,47 +1,119 @@
|
|||
# Contributing to Invoice Ninja
|
||||
# Contributing to CONTRIBUTING.md
|
||||
|
||||
Thanks for your contributions!
|
||||
First off, thanks for taking the time to contribute!
|
||||
|
||||
## Submit bug reports or feature requests
|
||||
All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions.
|
||||
|
||||
Please discuss the changes with us ahead of time to ensure they will be merged.
|
||||
|
||||
### Submit pull requests
|
||||
* [Fork](https://github.com/invoiceninja/invoiceninja#fork-destination-box) the [Invoice Ninja repository](https://github.com/invoiceninja/invoiceninja)
|
||||
* Create a new branch with the name `#issue_number-Short-description`
|
||||
* _Example:_ `#100-Add-GoogleAnalytics`
|
||||
* Make your changes and commit
|
||||
* Check if your branch is still in sync with the repositorys **`develop`** branch
|
||||
* _Read:_ [Syncing a fork](https://help.github.com/articles/syncing-a-fork/)
|
||||
* _Also read:_ [How to rebase a pull request](https://github.com/edx/edx-platform/wiki/How-to-Rebase-a-Pull-Request)
|
||||
* Push your branch and create a PR against the Invoice Ninja **`develop`** branch
|
||||
* Update the [Changelog](CHANGELOG.md)
|
||||
|
||||
### Some rules
|
||||
To make the contribution process nice and easy for anyone, please follow some rules:
|
||||
* Each contribution(bug or feature) should have an [issue on Github](https://github.com/invoiceninja/invoiceninja/issues)
|
||||
to give a more detailed explanation.
|
||||
* Only one feature/bugfix per issue. If you want to submit more, create multiple issues.
|
||||
* Only one feature/bugfix per PR(pull request). Split more changes into multiple PRs.
|
||||
|
||||
#### Coding Style
|
||||
Try to follow the [PSR-2 guidlines](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
|
||||
|
||||
_Example styling:_
|
||||
```php
|
||||
/**
|
||||
* Gets a preview of the email
|
||||
*
|
||||
* @param TemplateService $templateService
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function previewEmail(TemplateService $templateService)
|
||||
{
|
||||
//
|
||||
}
|
||||
```
|
||||
> And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about:
|
||||
> - Star the project
|
||||
> - Tweet about it
|
||||
> - Refer this project in your project's readme
|
||||
> - Mention the project at local meetups and tell your friends/colleagues
|
||||
|
||||
|
||||
## Translations
|
||||
For helping us with translating Invoice Ninja, please use [Transifex](https://www.transifex.com/invoice-ninja/invoice-ninja/).
|
||||
## Table of Contents
|
||||
|
||||
- [Code of Conduct](#code-of-conduct)
|
||||
- [I Have a Question](#i-have-a-question)
|
||||
- [I Want To Contribute](#i-want-to-contribute)
|
||||
- [Reporting Bugs](#reporting-bugs)
|
||||
- [Suggesting Enhancements](#suggesting-enhancements)
|
||||
- [Your First Code Contribution](#your-first-code-contribution)
|
||||
- [Improving The Documentation](#improving-the-documentation)
|
||||
- [Styleguides](#styleguides)
|
||||
- [Commit Messages](#commit-messages)
|
||||
- [Join The Project Team](#join-the-project-team)
|
||||
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project and everyone participating in it is governed by the
|
||||
[CONTRIBUTING.md Code of Conduct](blob/v5-stable/CODE_OF_CONDUCT.md).
|
||||
By participating, you are expected to uphold this code. Please report unacceptable behavior
|
||||
to <>.
|
||||
|
||||
|
||||
## I Have a Question
|
||||
|
||||
> If you want to ask a question, we assume that you have read the available [Documentation](https://invoiceninja.github.io), Searched the [Forum](https://forum.invoiceninja.com), or tried chatting with us on [Slack](https://invoiceninja.slack.com) [generate a slack invite here](http://slack.invoiceninja.com)
|
||||
|
||||
Please reserve issues for bugs, general questions posted in the issues section will be closed and redirected to other support venues.
|
||||
|
||||
## I Want To Contribute
|
||||
|
||||
|
||||
> ### Legal Notice
|
||||
> When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license. Please note that you'll need to sign the CLA for any PRs to be accepted
|
||||
|
||||
### Reporting Bugs
|
||||
|
||||
|
||||
#### Before Submitting a Bug Report
|
||||
|
||||
A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible.
|
||||
|
||||
- Make sure that you are using the latest version.
|
||||
- Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (Make sure that you have read the [documentation](https://invoiceninja.github.io). If you are looking for support, you might want to check [this section](#i-have-a-question)).
|
||||
- To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in the [bug tracker](issues?q=label%3Abug).
|
||||
- Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub community have discussed the issue.
|
||||
- Collect information about the bug:
|
||||
- Stack trace (Traceback)
|
||||
- OS, Platform and Version (Windows, Linux, macOS, x86, ARM)
|
||||
- Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant.
|
||||
- Possibly your input and the output
|
||||
- Can you reliably reproduce the issue? And can you also reproduce it with older versions?
|
||||
|
||||
|
||||
#### How Do I Submit a Good Bug Report?
|
||||
|
||||
> You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to <>.
|
||||
|
||||
|
||||
We use GitHub issues to track bugs and errors. If you run into an issue with the project:
|
||||
|
||||
- Open an [Issue](/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.)
|
||||
- Explain the behavior you would expect and the actual behavior.
|
||||
- Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case.
|
||||
- Provide the information you collected in the previous section.
|
||||
|
||||
Once it's filed:
|
||||
|
||||
- The project team will label the issue accordingly.
|
||||
- A team member will try to reproduce the issue with your provided steps. If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps and mark the issue as `needs-repro`. Bugs with the `needs-repro` tag will not be addressed until they are reproduced.
|
||||
- If the team is able to reproduce the issue, it will be marked `needs-fix`, as well as possibly other tags (such as `critical`), and the issue will be left to be [implemented by someone](#your-first-code-contribution).
|
||||
|
||||
|
||||
|
||||
|
||||
### Suggesting Enhancements
|
||||
|
||||
This section guides you through submitting an enhancement suggestion for CONTRIBUTING.md, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions.
|
||||
|
||||
|
||||
#### Before Submitting an Enhancement
|
||||
|
||||
- Make sure that you are using the latest version.
|
||||
- Read the [documentation]() carefully and find out if the functionality is already covered, maybe by an individual configuration.
|
||||
- Perform a [search](/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
|
||||
- Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library.
|
||||
|
||||
|
||||
#### How Do I Submit a Good Enhancement Suggestion?
|
||||
|
||||
Enhancement suggestions are tracked as [GitHub issues](/issues).
|
||||
|
||||
- Use a **clear and descriptive title** for the issue to identify the suggestion.
|
||||
- Provide a **step-by-step description of the suggested enhancement** in as many details as possible.
|
||||
- **Describe the current behavior** and **explain which behavior you expected to see instead** and why. At this point you can also tell which alternatives do not work for you.
|
||||
- You may want to **include screenshots and animated GIFs** which help you demonstrate the steps or point out the part which the suggestion is related to. You can use [this tool](https://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux.
|
||||
- **Explain why this enhancement would be useful** to most CONTRIBUTING.md users. You may also want to point out the other projects that solved it better and which could serve as inspiration.
|
||||
|
||||
|
||||
|
||||
### Your First Code Contribution
|
||||
|
||||
All PRs should be created against the v5-develop branch.
|
||||
|
||||
|
||||
## Attribution
|
||||
This guide is based on the **contributing.md**. [Make your own](https://contributing.md/)!
|
||||
|
|
|
|||
43
Gruntfile.js
vendored
43
Gruntfile.js
vendored
|
|
@ -1,43 +0,0 @@
|
|||
module.exports = function(grunt) {
|
||||
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
dump_dir: (function() {
|
||||
var out = {};
|
||||
|
||||
grunt.file.expand({ filter: 'isDirectory'}, 'public/fonts/invoice-fonts/*').forEach(function(path) {
|
||||
var fontName = /[^/]*$/.exec(path)[0],
|
||||
files = {},
|
||||
license='';
|
||||
|
||||
// Add license text
|
||||
grunt.file.expand({ filter: 'isFile'}, path+'/*.txt').forEach(function(path) {
|
||||
var licenseText = grunt.file.read(path);
|
||||
|
||||
// Fix anything that could escape from the comment
|
||||
licenseText = licenseText.replace(/\*\//g,'*\\/');
|
||||
|
||||
license += "/*\n"+licenseText+"\n*/";
|
||||
});
|
||||
|
||||
// Create files list
|
||||
files['public/js/vfs_fonts/'+fontName+'.js'] = [path+'/*.ttf'];
|
||||
|
||||
out[fontName] = {
|
||||
options: {
|
||||
pre: license+'window.ninjaFontVfs=window.ninjaFontVfs||{};window.ninjaFontVfs.'+fontName+'=',
|
||||
rootPath: path+'/'
|
||||
},
|
||||
files: files
|
||||
};
|
||||
});
|
||||
|
||||
// Return the computed object
|
||||
return out;
|
||||
}())
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-dump-dir');
|
||||
grunt.registerTask('default', ['dump_dir']);
|
||||
|
||||
};
|
||||
88
LICENSE
88
LICENSE
|
|
@ -1,47 +1,47 @@
|
|||
Copyright (c) 2018 by Hillel Coren
|
||||
Invoice Ninja * https://www.invoiceninja.com
|
||||
"CREATE. SEND. GET PAID"
|
||||
Elastic License 2.0 (ELv2)
|
||||
Elastic License
|
||||
|
||||
All Rights Reserved
|
||||
ATTRIBUTION ASSURANCE LICENSE (adapted from the original BSD license)
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the conditions below are met.
|
||||
These conditions require a modest attribution to InvoiceNinja.com (the
|
||||
"Author"), who hopes that its promotional value may help justify the
|
||||
thousands of dollars in otherwise billable time invested in writing
|
||||
this and other freely available, open-source software.
|
||||
Acceptance
|
||||
By using the software, you agree to all of the terms and conditions below.
|
||||
|
||||
1. Redistributions of source code, in whole or part and with or without
|
||||
modification (the "Code"), must prominently display this GPG-signed
|
||||
text in verifiable form.
|
||||
2. Redistributions of the Code in binary form must be accompanied by
|
||||
this GPG-signed text in any documentation and, each time the resulting
|
||||
executable program or a program dependent thereon is launched, a
|
||||
prominent display (e.g., splash screen or banner text) of the Author's
|
||||
attribution information, which includes:
|
||||
(a) Name ("Hillel Coren"),
|
||||
(b) Professional identification ("Invoice Ninja"), and
|
||||
(c) URL ("https://www.invoiceninja.com").
|
||||
3. Neither the name nor any trademark of the Author may be used to
|
||||
endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
4. Users are entirely responsible, to the exclusion of the Author and
|
||||
any other persons, for compliance with (1) regulations set by owners or
|
||||
administrators of employed equipment, (2) licensing terms of any other
|
||||
software, and (3) local regulations regarding use, including those
|
||||
regarding import, export, and use of encryption software.
|
||||
Copyright License
|
||||
The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable, non-transferable license to use, copy, distribute, make available, and prepare derivative works of the software, in each case subject to the limitations and conditions below
|
||||
|
||||
THIS FREE SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
EVENT SHALL THE AUTHOR OR ANY CONTRIBUTOR BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
EFFECTS OF UNAUTHORIZED OR MALICIOUS NETWORK ACCESS;
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Limitations
|
||||
You may not provide the software to third parties as a hosted or managed service, where the service provides users with access to any substantial set of the features or functionality of the software.
|
||||
|
||||
You may not move, change, disable, or circumvent the license key functionality in the software, and you may not remove or obscure any functionality in the software that is protected by the license key.
|
||||
|
||||
You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensor’s trademarks is subject to applicable law.
|
||||
|
||||
Patents
|
||||
The licensor grants you a license, under any patent claims the licensor can license, or becomes able to license, to make, have made, use, sell, offer for sale, import and have imported the software, in each case subject to the limitations and conditions in this license. This license does not cover any patent claims that you cause to be infringed by modifications or additions to the software. If you or your company make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company.
|
||||
|
||||
Notices
|
||||
You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms.
|
||||
|
||||
If you modify the software, you must include in any modified copies of the software prominent notices stating that you have modified the software.
|
||||
|
||||
No Other Rights
|
||||
These terms do not imply any licenses other than those expressly granted in these terms.
|
||||
|
||||
Termination
|
||||
If you use the software in violation of these terms, such use is not licensed, and your licenses will automatically terminate. If the licensor provides you with a notice of your violation, and you cease all violation of this license no later than 30 days after you receive that notice, your licenses will be reinstated retroactively. However, if you violate these terms after such reinstatement, any additional violation of these terms will cause your licenses to terminate automatically and permanently.
|
||||
|
||||
No Liability
|
||||
As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.
|
||||
|
||||
Definitions
|
||||
The licensor is the entity offering these terms, and the software is the software the licensor makes available under these terms, including any portion of it.
|
||||
|
||||
you refers to the individual or entity agreeing to these terms.
|
||||
|
||||
your company is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. control means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect.
|
||||
|
||||
your licenses are all the licenses granted to you for the software under these terms.
|
||||
|
||||
use means anything you do with the software requiring one of your licenses.
|
||||
|
||||
trademark means trademarks, service marks, and similar rights.
|
||||
|
||||
For more information regarding the interpretation of this license please see here: https://invoiceninja.github.io/docs/legal/license/
|
||||
127
README.md
127
README.md
|
|
@ -2,95 +2,100 @@
|
|||
<img src="https://raw.githubusercontent.com/hillelcoren/invoice-ninja/master/public/images/round_logo.png" alt="Sublime's custom image"/>
|
||||
</p>
|
||||
|
||||
# Invoice Ninja
|
||||

|
||||
[](https://www.codacy.com/gh/turbo124/invoiceninja/dashboard?utm_source=github.com&utm_medium=referral&utm_content=turbo124/invoiceninja&utm_campaign=Badge_Grade)
|
||||
<a href="https://cla-assistant.io/invoiceninja/invoiceninja"><img src="https://cla-assistant.io/readme/badge/invoiceninja/invoiceninja" alt="CLA assistant" /></a>
|
||||
|
||||
[](https://travis-ci.org/invoiceninja/invoiceninja)
|
||||
[](https://invoice-ninja.readthedocs.io/en/latest/?badge=latest)
|
||||
# Invoice Ninja 5
|
||||
|
||||
## [Hosted](https://www.invoiceninja.com) | [Self-Hosted](https://www.invoiceninja.org)
|
||||
|
||||
### We're on Slack, join us at [slack.invoiceninja.com](http://slack.invoiceninja.com) or if you like [StackOverflow](https://stackoverflow.com/tags/invoice-ninja/)
|
||||
Join us on [Slack](http://slack.invoiceninja.com), [Discord](https://discord.gg/ZwEdtfCwXA), [Support Forum](https://forum.invoiceninja.com)
|
||||
|
||||
Just make sure to add the `invoice-ninja` tag to your question.
|
||||
## Introduction
|
||||
|
||||
#### Note: v5 is now tagged Stable! To upgrade from v4 you need to [install v5](https://invoiceninja.github.io/docs/self-host-installation/) as a separate app and then use the migration tool in the latest version of v4 on Settings > Account Management.
|
||||
Version 5 of Invoice Ninja is here!
|
||||
We took the best parts of version 4 and add the most requested features
|
||||
to produce a invoicing application like no other.
|
||||
|
||||
All Pro and Enterprise features from the hosted app are included in the open-source code. We offer a $30 per year white-label license to remove the Invoice Ninja branding from client facing parts of the app.
|
||||
All Pro and Enterprise features from the hosted app are included in the open code.
|
||||
We offer a $30 per year white-label license to remove the Invoice Ninja branding from client facing parts of the app.
|
||||
|
||||
The self-host zip includes all third party libraries whereas downloading the code from GitHub requires using Composer to install the dependencies.
|
||||
* [Videos](https://www.youtube.com/@appinvoiceninja)
|
||||
* [API Documentation](https://api-docs.invoicing.co/)
|
||||
* [APP Documentation](https://invoiceninja.github.io/)
|
||||
* [Support Forum](https://forum.invoiceninja.com)
|
||||
|
||||
* [Features](https://www.invoiceninja.com/invoicing-features/)
|
||||
* [Videos](https://www.youtube.com/channel/UCXAHcBvhW05PDtWYIq7WDFA/videos)
|
||||
* [User Guide](https://docs.invoiceninja.com/)
|
||||
* [Support Forum](https://www.invoiceninja.com/forums/forum/support/)
|
||||
* [StackOverflow](https://stackoverflow.com/tags/invoice-ninja/)
|
||||
## Setup
|
||||
|
||||
## Referral Program
|
||||
* Earn 50% of Pro & Enterprise Plans up to 4 years - [Learn more](https://www.invoiceninja.com/referral-program/)
|
||||
### Mobile Apps
|
||||
* [iPhone](https://apps.apple.com/app/id1503970375?platform=iphone)
|
||||
* [Android](https://play.google.com/store/apps/details?id=com.invoiceninja.app)
|
||||
* [F-Droid](https://f-droid.org/en/packages/com.invoiceninja.app)
|
||||
|
||||
## Mobile App
|
||||
* [iPhone](https://itunes.apple.com/us/app/invoice-ninja/id1435514417?ls=1&mt=8)
|
||||
* [Android](https://play.google.com/store/apps/details?id=com.invoiceninja.flutter)
|
||||
* [Source Code](https://github.com/invoiceninja/flutter-mobile)
|
||||
### Desktop Apps
|
||||
* [macOS](https://apps.apple.com/app/id1503970375?platform=mac)
|
||||
* [Windows](https://microsoft.com/en-us/p/invoice-ninja/9n3f2bbcfdr6)
|
||||
* [Linux](https://snapcraft.io/invoiceninja)
|
||||
|
||||
## Installation Options
|
||||
* [Ansible](https://github.com/invoiceninja/ansible-installer)
|
||||
* [Self-Host Zip](https://docs.invoiceninja.com/install.html)
|
||||
### Installation Options
|
||||
* [Docker File](https://hub.docker.com/r/invoiceninja/invoiceninja/)
|
||||
* [Cloudron](https://cloudron.io/store/com.invoiceninja.cloudronapp.html)
|
||||
* [Softaculous](https://www.softaculous.com/apps/ecommerce/Invoice_Ninja)
|
||||
* [Lando](https://github.com/invoiceninja/invoiceninja/issues/2880)
|
||||
* [Yunohost](https://github.com/YunoHost-Apps/invoiceninja_ynh)
|
||||
|
||||
## Recommended Providers
|
||||
|
||||
### Recommended Providers
|
||||
* [Stripe](https://stripe.com/)
|
||||
* [Postmark](https://postmarkapp.com/)
|
||||
|
||||
## Development
|
||||
* [API Documentation](https://invoice-ninja.readthedocs.io/en/latest/api.html)
|
||||
* [PHP SDK](https://github.com/invoiceninja/sdk-php)
|
||||
* [Zend Framework](https://github.com/alexz707/InvoiceNinjaModule)
|
||||
* [Custom Module](https://invoice-ninja.readthedocs.io/en/latest/custom_modules.html) | [Watch Video](https://www.youtube.com/watch?v=8jJ-PYuq85k)
|
||||
## Quick Hosting Setup
|
||||
|
||||
## Third Party Modules
|
||||
* [Event Scheduler](https://github.com/cytech/Scheduler-InvoiceNinja)
|
||||
* [Manufacturer Module](https://github.com/dicarlosystems/manufacturer-invoiceninja)
|
||||
* [Point of Sale](https://github.com/dicarlosystems/pointofsale-invoiceninja)
|
||||
* [Invoice Design Import/Export](https://github.com/feyst/invoicedesignexport)
|
||||
```sh
|
||||
git clone https://github.com/invoiceninja/invoiceninja.git
|
||||
git checkout v5-stable
|
||||
cp .env.example .env
|
||||
composer i -o --no-dev
|
||||
php artisan key:generate
|
||||
```
|
||||
|
||||
> Feel free to email us for help if you're working on a module, we're happy to provide developer support.
|
||||
Please Note:
|
||||
Your APP_KEY in the .env file is used to encrypt data, if you lose this you will not be able to run the application.
|
||||
|
||||
## Third Party Tools
|
||||
* [InvoicePlane Import](https://github.com/turbo124/Plane2Ninja)
|
||||
* [Toggl Sync](https://github.com/Matth--/toggl-invoiceninja-sync)
|
||||
* [Shopping Cart](https://github.com/Scifabric/invoiceninjashoppingcart)
|
||||
Run if you want to load sample data, remember to configure .env
|
||||
```sh
|
||||
php artisan migrate:fresh --seed && php artisan db:seed && php artisan ninja:create-test-data
|
||||
```
|
||||
|
||||
## Third Party Developers
|
||||
* [Bold Compass](https://boldcompass.com/customize-invoice-ninja/)
|
||||
To run the web server
|
||||
```sh
|
||||
php artisan serve
|
||||
```
|
||||
|
||||
## Contributing
|
||||
All contributors are welcome!
|
||||
For information on how contribute to Invoice Ninja, please see our [contributing guide](CONTRIBUTING.md).
|
||||
Navigate to (replace localhost with the appropriate domain)
|
||||
```
|
||||
http://localhost:8000/setup - To setup your configuration if you did not load sample data.
|
||||
http://localhost:8000/ - For Administrator Logon
|
||||
|
||||
user: small@example.com
|
||||
pass: password
|
||||
|
||||
http://localhost:8000/client/login - For Client Portal
|
||||
|
||||
user: user@example.com
|
||||
pass: password
|
||||
```
|
||||
|
||||
## Credits
|
||||
* [Hillel Coren](https://hillelcoren.com/)
|
||||
* [David Bomba](https://github.com/turbo124)
|
||||
* [All contributors](https://github.com/invoiceninja/invoiceninja/graphs/contributors)
|
||||
* [Benjamin Beganović](https://github.com/beganovich)
|
||||
* [All Contributors](https://github.com/invoiceninja/invoiceninja/graphs/contributors)
|
||||
|
||||
**Special thanks to:**
|
||||
* [Troels Liebe Bentsen](https://github.com/tlbdk)
|
||||
* [Jeramy Simpson](https://github.com/JeramyMywork) - [MyWork](https://www.mywork.com.au)
|
||||
* [Sigitas Limontas](https://lt.linkedin.com/in/sigitaslimontas)
|
||||
* [Joshua Dwire](https://github.com/joshuadwire) - [Bold Compass](https://boldcompass.com/)
|
||||
* [Holger Lösken](https://github.com/codedge) - [codedge](http://codedge.de)
|
||||
* [Samuel Laulhau](https://github.com/lalop) - [Lalop](http://lalop.co/)
|
||||
* [Alexander Zamponi](https://github.com/alexz707)
|
||||
* [Matthieu Calie](https://github.com/Matth--)
|
||||
* [Christopher Di Carlo](https://github.com/dicarlosystems) - [Di Carlo Systems Inc.](https://www.dicarlosystems.ca)
|
||||
* [Kristian Feldsam](https://github.com/feldsam) - [FeldHost™](https://www.feldhost.net)
|
||||
* [Suhas Sunil Gaikwad](https://github.com/Suhas-Gaikwad)
|
||||
* [Mike Skaggs](https://github.com/titan-fail)
|
||||
## Security
|
||||
|
||||
If you find a security issue with this application, please send an email to contact@invoiceninja.com.
|
||||
Please follow responsible disclosure procedures if you detect an issue.
|
||||
For further information on responsible disclosure please read [here](https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html).
|
||||
|
||||
## License
|
||||
Invoice Ninja is released under the Attribution Assurance License.
|
||||
Invoice Ninja is released under the Elastic License.
|
||||
See [LICENSE](LICENSE) for details.
|
||||
|
|
|
|||
1
VERSION.txt
Normal file
1
VERSION.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
5.5.88
|
||||
17
_ide_helper_custom.php
Normal file
17
_ide_helper_custom.php
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Illuminate\Contracts\Mail
|
||||
{
|
||||
class Mailer
|
||||
{
|
||||
public function postmark_config(string $key)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function mailgun_config(string $key)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Commands;
|
||||
|
||||
abstract class Command
|
||||
{
|
||||
}
|
||||
204
app/Console/Commands/BackupUpdate.php
Normal file
204
app/Console/Commands/BackupUpdate.php
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Backup;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\Document;
|
||||
use App\Models\GroupSetting;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class BackupUpdate extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:backup-files {--disk=}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Shift files between object storage locations';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
//always return state to first DB
|
||||
|
||||
$current_db = config('database.default');
|
||||
|
||||
if (! config('ninja.db.multi_db_enabled')) {
|
||||
$this->handleOnDb();
|
||||
} else {
|
||||
//multiDB environment, need to
|
||||
foreach (MultiDB::$dbs as $db) {
|
||||
MultiDB::setDB($db);
|
||||
|
||||
$this->handleOnDb();
|
||||
}
|
||||
|
||||
MultiDB::setDB($current_db);
|
||||
}
|
||||
}
|
||||
|
||||
private function handleOnDb()
|
||||
{
|
||||
set_time_limit(0);
|
||||
|
||||
//logos
|
||||
Company::cursor()
|
||||
->each(function ($company) {
|
||||
$company_logo_path = $company->settings->company_logo;
|
||||
|
||||
if ($company_logo_path == 'https://invoicing.co/images/new_logo.png' || $company_logo_path == '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$logo = @file_get_contents($company_logo_path);
|
||||
$extension = @pathinfo($company->settings->company_logo, PATHINFO_EXTENSION);
|
||||
|
||||
if ($logo && $extension) {
|
||||
$path = "{$company->company_key}/{$company->company_key}.{$extension}";
|
||||
|
||||
Storage::disk($this->option('disk'))->put($path, $logo);
|
||||
|
||||
$url = Storage::disk($this->option('disk'))->url($path);
|
||||
|
||||
nlog("Company - Moving {$company_logo_path} logo to {$this->option('disk')} final URL = {$url}}");
|
||||
|
||||
$settings = $company->settings;
|
||||
$settings->company_logo = $url;
|
||||
$company->settings = $settings;
|
||||
;
|
||||
$company->save();
|
||||
}
|
||||
});
|
||||
|
||||
Client::withTrashed()
|
||||
->whereNotNull('settings->company_logo')
|
||||
->cursor()
|
||||
->each(function ($client) {
|
||||
$company_logo_path = $client->settings->company_logo;
|
||||
|
||||
$logo = @file_get_contents($company_logo_path);
|
||||
$extension = @pathinfo($company_logo_path, PATHINFO_EXTENSION);
|
||||
|
||||
if ($logo && $extension) {
|
||||
$path = "{$client->company->company_key}/{$client->client_hash}.{$extension}";
|
||||
|
||||
Storage::disk($this->option('disk'))->put($path, $logo);
|
||||
|
||||
$url = Storage::disk($this->option('disk'))->url($path);
|
||||
|
||||
nlog("Client - Moving {$company_logo_path} logo to {$this->option('disk')} final URL = {$url}}");
|
||||
|
||||
$settings = $client->settings;
|
||||
$settings->company_logo = $url;
|
||||
$client->settings = $settings;
|
||||
;
|
||||
$client->saveQuietly();
|
||||
}
|
||||
});
|
||||
|
||||
GroupSetting::withTrashed()
|
||||
->whereNotNull('settings->company_logo')
|
||||
->orWhere('settings->company_logo', '!=', '')
|
||||
->cursor()
|
||||
->each(function ($group) {
|
||||
$company_logo_path = $group->settings->company_logo;
|
||||
|
||||
if (!$company_logo_path) {
|
||||
return;
|
||||
}
|
||||
|
||||
$logo = @file_get_contents($company_logo_path);
|
||||
$extension = @pathinfo($company_logo_path, PATHINFO_EXTENSION);
|
||||
|
||||
if ($logo && $extension) {
|
||||
$path = "{$group->company->company_key}/{$group->hashed_id}.{$extension}";
|
||||
|
||||
Storage::disk($this->option('disk'))->put($path, $logo);
|
||||
|
||||
$url = Storage::disk($this->option('disk'))->url($path);
|
||||
|
||||
nlog("Group - Moving {$company_logo_path} logo to {$this->option('disk')} final URL = {$url}}");
|
||||
|
||||
$settings = $group->settings;
|
||||
$settings->company_logo = $url;
|
||||
$group->settings = $settings;
|
||||
;
|
||||
$group->saveQuietly();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
//documents
|
||||
Document::cursor()
|
||||
->each(function (Document $document) {
|
||||
$doc_bin = false;
|
||||
|
||||
try {
|
||||
$doc_bin = $document->getFile();
|
||||
} catch(\Exception $e) {
|
||||
nlog($e->getMessage());
|
||||
}
|
||||
|
||||
if ($doc_bin) {
|
||||
Storage::disk($this->option('disk'))->put($document->url, $doc_bin);
|
||||
|
||||
$document->disk = $this->option('disk');
|
||||
$document->saveQuietly();
|
||||
|
||||
nlog("Documents - Moving {$document->url} to {$this->option('disk')}");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//backups
|
||||
Backup::whereNotNull('filename')
|
||||
->where('filename', '!=', '')
|
||||
->cursor()
|
||||
->each(function ($backup) {
|
||||
|
||||
$backup_bin = Storage::disk('s3')->get($backup->filename);
|
||||
|
||||
if ($backup_bin) {
|
||||
Storage::disk($this->option('disk'))->put($backup->filename, $backup_bin);
|
||||
|
||||
nlog("Backups - Moving {$backup->filename} to {$this->option('disk')}");
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\DbServer;
|
||||
use App\Models\User;
|
||||
use App\Models\Company;
|
||||
use App\Libraries\CurlUtils;
|
||||
|
||||
class CalculatePayouts extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:calculate-payouts {--type=} {--url=} {--password=}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Calculate payouts';
|
||||
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$type = strtolower($this->option('type'));
|
||||
|
||||
switch ($type) {
|
||||
case 'referral':
|
||||
$this->referralPayouts();
|
||||
break;
|
||||
case 'reseller':
|
||||
$this->resellerPayouts();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function referralPayouts()
|
||||
{
|
||||
$servers = DbServer::orderBy('id')->get(['name']);
|
||||
$userMap = [];
|
||||
|
||||
foreach ($servers as $server) {
|
||||
config(['database.default' => $server->name]);
|
||||
|
||||
$users = User::where('referral_code', '!=', '')
|
||||
->get(['email', 'referral_code']);
|
||||
foreach ($users as $user) {
|
||||
$userMap[$user->referral_code] = $user->email;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($servers as $server) {
|
||||
config(['database.default' => $server->name]);
|
||||
|
||||
$companies = Company::where('referral_code', '!=', '')
|
||||
->with('payment.client.payments')
|
||||
->whereNotNull('payment_id')
|
||||
->get();
|
||||
|
||||
$this->info('User,Client,Date,Amount,Reference');
|
||||
|
||||
foreach ($companies as $company) {
|
||||
if (!isset($userMap[$company->referral_code])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$user = $userMap[$company->referral_code];
|
||||
$payment = $company->payment;
|
||||
|
||||
if ($payment) {
|
||||
$client = $payment->client;
|
||||
|
||||
foreach ($client->payments as $payment) {
|
||||
$amount = $payment->getCompletedAmount();
|
||||
$this->info('"' . $user . '",' .
|
||||
'"' . $client->getDisplayName() . '",' .
|
||||
$payment->payment_date . ',' .
|
||||
$amount . ',' .
|
||||
$payment->transaction_reference
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function resellerPayouts()
|
||||
{
|
||||
$response = CurlUtils::post($this->option('url') . '/reseller_stats', [
|
||||
'password' => $this->option('password')
|
||||
]);
|
||||
|
||||
$this->info('Response:');
|
||||
$this->info($response);
|
||||
}
|
||||
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['type', null, InputOption::VALUE_OPTIONAL, 'Type', null],
|
||||
['url', null, InputOption::VALUE_OPTIONAL, 'Url', null],
|
||||
['password', null, InputOption::VALUE_OPTIONAL, 'Password', null],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Account;
|
||||
use App\Models\Invoice;
|
||||
use App\Ninja\Mailers\ContactMailer as Mailer;
|
||||
use App\Ninja\Repositories\AccountRepository;
|
||||
use App\Services\PaymentService;
|
||||
use Illuminate\Console\Command;
|
||||
use Carbon;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Class ChargeRenewalInvoices.
|
||||
*/
|
||||
class ChargeRenewalInvoices extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'ninja:charge-renewals';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Charge renewal invoices';
|
||||
|
||||
/**
|
||||
* @var Mailer
|
||||
*/
|
||||
protected $mailer;
|
||||
|
||||
/**
|
||||
* @var AccountRepository
|
||||
*/
|
||||
protected $accountRepo;
|
||||
|
||||
/**
|
||||
* @var PaymentService
|
||||
*/
|
||||
protected $paymentService;
|
||||
|
||||
/**
|
||||
* ChargeRenewalInvoices constructor.
|
||||
*
|
||||
* @param Mailer $mailer
|
||||
* @param AccountRepository $repo
|
||||
* @param PaymentService $paymentService
|
||||
*/
|
||||
public function __construct(Mailer $mailer, AccountRepository $repo, PaymentService $paymentService)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->mailer = $mailer;
|
||||
$this->accountRepo = $repo;
|
||||
$this->paymentService = $paymentService;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->info(date('r').' ChargeRenewalInvoices...');
|
||||
|
||||
if ($database = $this->option('database')) {
|
||||
config(['database.default' => $database]);
|
||||
}
|
||||
|
||||
$ninjaAccount = $this->accountRepo->getNinjaAccount();
|
||||
$invoices = Invoice::whereAccountId($ninjaAccount->id)
|
||||
->whereDueDate(date('Y-m-d'))
|
||||
->where('balance', '>', 0)
|
||||
->with('client')
|
||||
->orderBy('id')
|
||||
->get();
|
||||
|
||||
$this->info($invoices->count() . ' invoices found');
|
||||
|
||||
foreach ($invoices as $invoice) {
|
||||
|
||||
// check if account has switched to free since the invoice was created
|
||||
$account = Account::find($invoice->client->public_id);
|
||||
|
||||
if (! $account) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$company = $account->company;
|
||||
if (! $company->plan || $company->plan == PLAN_FREE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Carbon::parse($company->plan_expires)->isFuture()) {
|
||||
$this->info('Skipping invoice ' . $invoice->invoice_number . ' [plan not expired]');
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->info("Charging invoice {$invoice->invoice_number}");
|
||||
if (! $this->paymentService->autoBillInvoice($invoice)) {
|
||||
$this->info('Failed to auto-bill, emailing invoice');
|
||||
$this->mailer->sendInvoice($invoice);
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('Done');
|
||||
|
||||
if ($errorEmail = env('ERROR_EMAIL')) {
|
||||
\Mail::raw('EOM', function ($message) use ($errorEmail) {
|
||||
$message->to($errorEmail)
|
||||
->from(CONTACT_EMAIL)
|
||||
->subject('ChargeRenewalInvoices: Finished successfully');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getArguments()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['database', null, InputOption::VALUE_OPTIONAL, 'Database', null],
|
||||
];
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
143
app/Console/Commands/CheckDb.php
Normal file
143
app/Console/Commands/CheckDb.php
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Account;
|
||||
use App\Models\Activity;
|
||||
use App\Models\Backup;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\CompanyLedger;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\CompanyUser;
|
||||
use App\Models\Credit;
|
||||
use App\Models\CreditInvitation;
|
||||
use App\Models\Design;
|
||||
use App\Models\Document;
|
||||
use App\Models\Expense;
|
||||
use App\Models\ExpenseCategory;
|
||||
use App\Models\Gateway;
|
||||
use App\Models\GroupSetting;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\InvoiceInvitation;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Paymentable;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\Product;
|
||||
use App\Models\Project;
|
||||
use App\Models\Quote;
|
||||
use App\Models\QuoteInvitation;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\RecurringInvoiceInvitation;
|
||||
use App\Models\Subscription;
|
||||
use App\Models\SystemLog;
|
||||
use App\Models\Task;
|
||||
use App\Models\TaskStatus;
|
||||
use App\Models\TaxRate;
|
||||
use App\Models\User;
|
||||
use App\Models\Vendor;
|
||||
use App\Models\VendorContact;
|
||||
use App\Models\Webhook;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Class CheckDb.
|
||||
*/
|
||||
class CheckDb extends Command
|
||||
{
|
||||
protected $signature = 'ninja:check-db';
|
||||
|
||||
protected $description = 'Check MultiDB';
|
||||
|
||||
protected $log = '';
|
||||
|
||||
private $entities = [
|
||||
Account::class,
|
||||
Activity::class,
|
||||
Backup::class,
|
||||
Client::class,
|
||||
ClientContact::class,
|
||||
ClientGatewayToken::class,
|
||||
Company::class,
|
||||
CompanyGateway::class,
|
||||
CompanyLedger::class,
|
||||
CompanyToken::class,
|
||||
CompanyUser::class,
|
||||
Credit::class,
|
||||
CreditInvitation::class,
|
||||
Design::class,
|
||||
Document::class,
|
||||
Expense::class,
|
||||
ExpenseCategory::class,
|
||||
Gateway::class,
|
||||
GroupSetting::class,
|
||||
Invoice::class,
|
||||
InvoiceInvitation::class,
|
||||
Payment::class,
|
||||
Paymentable::class,
|
||||
PaymentHash::class,
|
||||
Product::class,
|
||||
Project::class,
|
||||
Quote::class,
|
||||
QuoteInvitation::class,
|
||||
RecurringInvoice::class,
|
||||
RecurringInvoiceInvitation::class,
|
||||
Subscription::class,
|
||||
SystemLog::class,
|
||||
Task::class,
|
||||
TaskStatus::class,
|
||||
TaxRate::class,
|
||||
User::class,
|
||||
Vendor::class,
|
||||
VendorContact::class,
|
||||
WebHook::class,
|
||||
];
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->LogMessage('Checking - V5_DB1');
|
||||
|
||||
foreach ($this->entities as $entity) {
|
||||
$count_db_1 = $entity::on('db-ninja-01')->count();
|
||||
$count_db_2 = $entity::on('db-ninja-02a')->count();
|
||||
|
||||
$diff = $count_db_1 - $count_db_2;
|
||||
|
||||
if ($diff != 0) {
|
||||
$this->logMessage("{$entity} DB1: {$count_db_1} - DB2: {$count_db_2} - diff = {$diff}");
|
||||
}
|
||||
}
|
||||
|
||||
$this->LogMessage('Checking - V5_DB2');
|
||||
|
||||
foreach ($this->entities as $entity) {
|
||||
$count_db_1 = $entity::on('db-ninja-02')->count();
|
||||
$count_db_2 = $entity::on('db-ninja-01a')->count();
|
||||
|
||||
$diff = $count_db_1 - $count_db_2;
|
||||
|
||||
if ($diff != 0) {
|
||||
$this->logMessage("{$entity} DB1: {$count_db_1} - DB2: {$count_db_2} - diff = {$diff}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function logMessage($str)
|
||||
{
|
||||
$str = date('Y-m-d h:i:s').' '.$str;
|
||||
$this->info($str);
|
||||
$this->log .= $str."\n";
|
||||
}
|
||||
}
|
||||
145
app/Console/Commands/CreateAccount.php
Normal file
145
app/Console/Commands/CreateAccount.php
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\DataMapper\ClientRegistrationFields;
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Jobs\Company\CreateCompanyPaymentTerms;
|
||||
use App\Jobs\Company\CreateCompanyTaskStatuses;
|
||||
use App\Jobs\Util\VersionCheck;
|
||||
use App\Models\Account;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\User;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class CreateAccount extends Command
|
||||
{
|
||||
use MakesHash, GeneratesCounter;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create Single Account';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:create-account {--email=} {--password=}';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @param InvoiceRepository $invoice_repo
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info(date('r').' Create Single Account...');
|
||||
|
||||
$this->warmCache();
|
||||
|
||||
$this->createAccount();
|
||||
}
|
||||
|
||||
private function createAccount()
|
||||
{
|
||||
$account = Account::factory()->create();
|
||||
$company = Company::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'portal_domain' => config('ninja.app_url'),
|
||||
'portal_mode' => 'domain',
|
||||
]);
|
||||
|
||||
$company->client_registration_fields = ClientRegistrationFields::generate();
|
||||
$company->save();
|
||||
|
||||
$account->default_company_id = $company->id;
|
||||
$account->save();
|
||||
|
||||
$email = $this->option('email') ?? 'admin@example.com';
|
||||
$password = $this->option('password') ?? 'changeme!';
|
||||
|
||||
$user = User::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'email' => $email,
|
||||
'password' => Hash::make($password),
|
||||
'confirmation_code' => $this->createDbHash(config('database.default')),
|
||||
'email_verified_at' => now(),
|
||||
'first_name' => 'New',
|
||||
'last_name' => 'User',
|
||||
'phone' => '',
|
||||
]);
|
||||
|
||||
$company_token = new CompanyToken;
|
||||
$company_token->user_id = $user->id;
|
||||
$company_token->company_id = $company->id;
|
||||
$company_token->account_id = $account->id;
|
||||
$company_token->name = 'User Token';
|
||||
$company_token->token = Str::random(64);
|
||||
$company_token->is_system = true;
|
||||
|
||||
$company_token->save();
|
||||
|
||||
$user->companies()->attach($company->id, [
|
||||
'account_id' => $account->id,
|
||||
'is_owner' => 1,
|
||||
'is_admin' => 1,
|
||||
'is_locked' => 0,
|
||||
'notifications' => CompanySettings::notificationDefaults(),
|
||||
'settings' => null,
|
||||
]);
|
||||
|
||||
(new CreateCompanyPaymentTerms($company, $user))->handle();
|
||||
(new CreateCompanyTaskStatuses($company, $user))->handle();
|
||||
(new VersionCheck())->handle();
|
||||
|
||||
$this->warmCache();
|
||||
}
|
||||
|
||||
private function warmCache()
|
||||
{
|
||||
/* Warm up the cache !*/
|
||||
$cached_tables = config('ninja.cached_tables');
|
||||
|
||||
foreach ($cached_tables as $name => $class) {
|
||||
if ($name == 'payment_terms') {
|
||||
$orderBy = 'num_days';
|
||||
} elseif ($name == 'fonts') {
|
||||
$orderBy = 'sort_order';
|
||||
} elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
|
||||
$orderBy = 'name';
|
||||
} else {
|
||||
$orderBy = 'id';
|
||||
}
|
||||
$tableData = $class::orderBy($orderBy)->get();
|
||||
if ($tableData->count()) {
|
||||
Cache::forever($name, $tableData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,223 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Utils;
|
||||
use stdClass;
|
||||
use App\Models\Account;
|
||||
use Faker\Factory;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Class CreateLuisData.
|
||||
*/
|
||||
class CreateLuisData extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create LUIS Data';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:create-luis-data {faker_field=name}';
|
||||
|
||||
/**
|
||||
* CreateLuisData constructor.
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->faker = Factory::create();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->fakerField = $this->argument('faker_field');
|
||||
|
||||
$intents = [];
|
||||
$entityTypes = [
|
||||
ENTITY_INVOICE,
|
||||
ENTITY_QUOTE,
|
||||
ENTITY_CLIENT,
|
||||
ENTITY_CREDIT,
|
||||
ENTITY_EXPENSE,
|
||||
ENTITY_PAYMENT,
|
||||
ENTITY_PRODUCT,
|
||||
ENTITY_RECURRING_INVOICE,
|
||||
ENTITY_TASK,
|
||||
ENTITY_VENDOR,
|
||||
];
|
||||
|
||||
foreach ($entityTypes as $entityType) {
|
||||
$intents = array_merge($intents, $this->createIntents($entityType));
|
||||
}
|
||||
|
||||
$intents = array_merge($intents, $this->getNavigateToIntents($entityType));
|
||||
|
||||
$this->info(json_encode($intents));
|
||||
}
|
||||
|
||||
private function createIntents($entityType)
|
||||
{
|
||||
$intents = [];
|
||||
|
||||
$intents = array_merge($intents, $this->getCreateEntityIntents($entityType));
|
||||
$intents = array_merge($intents, $this->getFindEntityIntents($entityType));
|
||||
$intents = array_merge($intents, $this->getListEntityIntents($entityType));
|
||||
|
||||
return $intents;
|
||||
}
|
||||
|
||||
private function getCreateEntityIntents($entityType)
|
||||
{
|
||||
$intents = [];
|
||||
$phrases = [
|
||||
"create new {$entityType}",
|
||||
"new {$entityType}",
|
||||
"make a {$entityType}",
|
||||
];
|
||||
|
||||
foreach ($phrases as $phrase) {
|
||||
$intents[] = $this->createIntent('CreateEntity', $phrase, [
|
||||
$entityType => 'EntityType',
|
||||
]);
|
||||
if ($entityType != ENTITY_CLIENT) {
|
||||
$client = $this->faker->{$this->fakerField};
|
||||
$phrase .= " for {$client}";
|
||||
$intents[] = $this->createIntent('CreateEntity', $phrase, [
|
||||
$entityType => 'EntityType',
|
||||
$client => 'Name',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $intents;
|
||||
}
|
||||
|
||||
private function getFindEntityIntents($entityType)
|
||||
{
|
||||
$intents = [];
|
||||
|
||||
if (in_array($entityType, [ENTITY_CLIENT, ENTITY_INVOICE, ENTITY_QUOTE])) {
|
||||
$name = $entityType === ENTITY_CLIENT ? $this->faker->{$this->fakerField} : $this->faker->randomNumber(4);
|
||||
$intents[] = $this->createIntent('FindEntity', "find {$entityType} {$name}", [
|
||||
$entityType => 'EntityType',
|
||||
$name => 'Name',
|
||||
]);
|
||||
if ($entityType === ENTITY_CLIENT) {
|
||||
$name = $this->faker->{$this->fakerField};
|
||||
$intents[] = $this->createIntent('FindEntity', "find {$name}", [
|
||||
$name => 'Name',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $intents;
|
||||
}
|
||||
|
||||
private function getListEntityIntents($entityType)
|
||||
{
|
||||
$intents = [];
|
||||
$entityTypePlural = Utils::pluralizeEntityType($entityType);
|
||||
|
||||
$intents[] = $this->createIntent('ListEntity', "show me {$entityTypePlural}", [
|
||||
$entityTypePlural => 'EntityType',
|
||||
]);
|
||||
$intents[] = $this->createIntent('ListEntity', "list {$entityTypePlural}", [
|
||||
$entityTypePlural => 'EntityType',
|
||||
]);
|
||||
|
||||
$intents[] = $this->createIntent('ListEntity', "show me active {$entityTypePlural}", [
|
||||
$entityTypePlural => 'EntityType',
|
||||
'active' => 'Filter',
|
||||
]);
|
||||
$intents[] = $this->createIntent('ListEntity', "list archived and deleted {$entityTypePlural}", [
|
||||
$entityTypePlural => 'EntityType',
|
||||
'archived' => 'Filter',
|
||||
'deleted' => 'Filter',
|
||||
]);
|
||||
|
||||
if ($entityType != ENTITY_CLIENT) {
|
||||
$client = $this->faker->{$this->fakerField};
|
||||
$intents[] = $this->createIntent('ListEntity', "list {$entityTypePlural} for {$client}", [
|
||||
$entityTypePlural => 'EntityType',
|
||||
$client => 'Name',
|
||||
]);
|
||||
$intents[] = $this->createIntent('ListEntity', "show me {$client}'s {$entityTypePlural}", [
|
||||
$entityTypePlural => 'EntityType',
|
||||
$client . '\'s' => 'Name',
|
||||
]);
|
||||
$intents[] = $this->createIntent('ListEntity', "show me {$client}'s active {$entityTypePlural}", [
|
||||
$entityTypePlural => 'EntityType',
|
||||
$client . '\'s' => 'Name',
|
||||
'active' => 'Filter',
|
||||
]);
|
||||
}
|
||||
|
||||
return $intents;
|
||||
}
|
||||
|
||||
private function getNavigateToIntents($entityType)
|
||||
{
|
||||
$intents = [];
|
||||
$locations = array_merge(Account::$basicSettings, Account::$advancedSettings);
|
||||
|
||||
foreach ($locations as $location) {
|
||||
$location = str_replace('_', ' ', $location);
|
||||
$intents[] = $this->createIntent('NavigateTo', "go to {$location}", [
|
||||
$location => 'Location',
|
||||
]);
|
||||
$intents[] = $this->createIntent('NavigateTo', "show me {$location}", [
|
||||
$location => 'Location',
|
||||
]);
|
||||
}
|
||||
|
||||
return $intents;
|
||||
}
|
||||
|
||||
private function createIntent($name, $text, $entities)
|
||||
{
|
||||
$intent = new stdClass();
|
||||
$intent->intent = $name;
|
||||
$intent->text = $text;
|
||||
$intent->entities = [];
|
||||
|
||||
foreach ($entities as $value => $entity) {
|
||||
$startPos = strpos($text, (string)$value);
|
||||
if (! $startPos) {
|
||||
dd("Failed to find {$value} in {$text}");
|
||||
}
|
||||
$entityClass = new stdClass();
|
||||
$entityClass->entity = $entity;
|
||||
$entityClass->startPos = $startPos;
|
||||
$entityClass->endPos = $entityClass->startPos + strlen($value) - 1;
|
||||
$intent->entities[] = $entityClass;
|
||||
}
|
||||
|
||||
return $intent;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getArguments()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
907
app/Console/Commands/CreateSingleAccount.php
Normal file
907
app/Console/Commands/CreateSingleAccount.php
Normal file
|
|
@ -0,0 +1,907 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\DataMapper\ClientRegistrationFields;
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\DataMapper\FeesAndLimits;
|
||||
use App\Events\Invoice\InvoiceWasCreated;
|
||||
use App\Events\RecurringInvoice\RecurringInvoiceWasCreated;
|
||||
use App\Factory\GroupSettingFactory;
|
||||
use App\Factory\InvoiceFactory;
|
||||
use App\Factory\InvoiceItemFactory;
|
||||
use App\Factory\RecurringInvoiceFactory;
|
||||
use App\Factory\SubscriptionFactory;
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use App\Jobs\Company\CreateCompanyTaskStatuses;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Account;
|
||||
use App\Models\BankIntegration;
|
||||
use App\Models\BankTransaction;
|
||||
use App\Models\BankTransactionRule;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\Country;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Product;
|
||||
use App\Models\Project;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Task;
|
||||
use App\Models\TaxRate;
|
||||
use App\Models\User;
|
||||
use App\Models\Vendor;
|
||||
use App\Models\VendorContact;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Carbon\Carbon;
|
||||
use Faker\Factory;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use stdClass;
|
||||
|
||||
class CreateSingleAccount extends Command
|
||||
{
|
||||
use MakesHash, GeneratesCounter;
|
||||
|
||||
protected $description = 'Create Single Sample Account';
|
||||
|
||||
protected $signature = 'ninja:create-single-account {gateway=all} {--database=db-ninja-01}';
|
||||
|
||||
protected $invoice_repo;
|
||||
|
||||
protected $count;
|
||||
|
||||
protected $gateway;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (Ninja::isHosted() || config('ninja.is_docker') || !$this->confirm('Are you sure you want to inject dummy data?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->invoice_repo = new InvoiceRepository();
|
||||
|
||||
MultiDB::setDb($this->option('database'));
|
||||
|
||||
$this->info(date('r').' Create Single Sample Account...');
|
||||
$this->count = 5;
|
||||
$this->gateway = $this->argument('gateway');
|
||||
|
||||
$this->info('Warming up cache');
|
||||
|
||||
$this->warmCache();
|
||||
|
||||
$this->createSmallAccount();
|
||||
}
|
||||
|
||||
private function createSmallAccount()
|
||||
{
|
||||
$this->info('Creating Small Account and Company');
|
||||
|
||||
if ($user = User::where('email', 'small@example.com')->first()) {
|
||||
$user->account->delete();
|
||||
}
|
||||
|
||||
$account = Account::factory()->create();
|
||||
$company = Company::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'slack_webhook_url' => config('ninja.notification.slack'),
|
||||
'default_password_timeout' => 30*60000,
|
||||
'portal_mode' => 'domain',
|
||||
'portal_domain' => 'http://ninja.test:8000',
|
||||
'track_inventory' => true
|
||||
]);
|
||||
$faker = \Faker\Factory::create();
|
||||
|
||||
$settings = $company->settings;
|
||||
$settings->invoice_terms = 'Default company invoice terms';
|
||||
$settings->quote_terms = 'Default company quote terms';
|
||||
$settings->invoice_footer = 'Default invoice footer';
|
||||
|
||||
$settings->company_logo = 'https://pdf.invoicing.co/favicon-v2.png';
|
||||
$settings->name = $faker->name();
|
||||
$settings->email = $faker->safeEmail();
|
||||
$settings->phone = $faker->phoneNumber();
|
||||
$settings->website = $faker->url();
|
||||
|
||||
$settings->address1 = $faker->streetName();
|
||||
$settings->address2 = $faker->streetAddress();
|
||||
$settings->city = $faker->city();
|
||||
$settings->state = $faker->state();
|
||||
$settings->postal_code = $faker->postcode();
|
||||
|
||||
$settings->country_id = '840';
|
||||
$settings->vat_number = 'vat number';
|
||||
$settings->id_number = 'id number';
|
||||
$settings->use_credits_payment = 'always';
|
||||
$settings->timezone_id = '1';
|
||||
$settings->entity_send_time = 0;
|
||||
$settings->name = $faker->name();
|
||||
|
||||
$company->settings = $settings;
|
||||
$company->client_registration_fields = ClientRegistrationFields::generate();
|
||||
$company->save();
|
||||
|
||||
$account->default_company_id = $company->id;
|
||||
$account->save();
|
||||
|
||||
$user = User::whereEmail('small@example.com')->first();
|
||||
|
||||
if (! $user) {
|
||||
$user = User::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'email' => 'small@example.com',
|
||||
'confirmation_code' => $this->createDbHash(config('database.default')),
|
||||
]);
|
||||
}
|
||||
|
||||
$company_token = new CompanyToken;
|
||||
$company_token->user_id = $user->id;
|
||||
$company_token->company_id = $company->id;
|
||||
$company_token->account_id = $account->id;
|
||||
$company_token->name = 'test token';
|
||||
$company_token->token = 'company-token-test';
|
||||
$company_token->is_system = true;
|
||||
|
||||
$company_token->save();
|
||||
|
||||
$user->companies()->attach($company->id, [
|
||||
'account_id' => $account->id,
|
||||
'is_owner' => 1,
|
||||
'is_admin' => 1,
|
||||
'is_locked' => 0,
|
||||
'notifications' => CompanySettings::notificationDefaults(),
|
||||
'settings' => null,
|
||||
]);
|
||||
|
||||
Product::factory()->count(1)->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
|
||||
TaxRate::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
'name' => 'GST',
|
||||
'rate' => 10
|
||||
]);
|
||||
|
||||
TaxRate::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
'name' => 'VAT',
|
||||
'rate' => 17.5
|
||||
]);
|
||||
|
||||
TaxRate::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
'name' => 'CA Sales Tax',
|
||||
'rate' => 5
|
||||
]);
|
||||
|
||||
$bi = BankIntegration::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'company_id' => $company->id,
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
|
||||
BankTransaction::factory()->count(50)->create([
|
||||
'bank_integration_id' => $bi->id,
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
$btr = BankTransactionRule::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
'applies_to' => (bool)rand(0, 1) ? 'CREDIT' : 'DEBIT',
|
||||
]);
|
||||
|
||||
$client = Client::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
'name' => 'cypress'
|
||||
]);
|
||||
|
||||
ClientContact::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'client_id' => $client->id,
|
||||
'company_id' => $company->id,
|
||||
'is_primary' => 1,
|
||||
'email' => 'cypress@example.com',
|
||||
'password' => Hash::make('password'),
|
||||
]);
|
||||
|
||||
|
||||
$this->info('Creating '.$this->count.' clients');
|
||||
|
||||
for ($x = 0; $x < $this->count; $x++) {
|
||||
$z = $x + 1;
|
||||
$this->info('Creating client # '.$z);
|
||||
|
||||
$this->createClient($company, $user);
|
||||
}
|
||||
|
||||
(new CreateCompanyTaskStatuses($company, $user))->handle();
|
||||
|
||||
for ($x = 0; $x < $this->count; $x++) {
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating invoice for client #'.$client->id);
|
||||
$this->createInvoice($client);
|
||||
$this->info('creating invoice for client #'.$client->id);
|
||||
$this->createInvoice($client);
|
||||
$this->info('creating invoice for client #'.$client->id);
|
||||
$this->createInvoice($client);
|
||||
$this->info('creating invoice for client #'.$client->id);
|
||||
$this->createInvoice($client);
|
||||
$this->info('creating invoice for client #'.$client->id);
|
||||
$this->createInvoice($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
// $this->info('creating credit for client #'.$client->id);
|
||||
// $this->createCredit($client); /** Prevents Stripe from running payments. */
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating quote for client #'.$client->id);
|
||||
$this->createQuote($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating expense for client #'.$client->id);
|
||||
$this->createExpense($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating vendor for client #'.$client->id);
|
||||
$this->createVendor($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating task for client #' . $client->id);
|
||||
$this->createTask($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating project for client #' . $client->id);
|
||||
$this->createProject($client);
|
||||
|
||||
$this->info('creating credit for client #' . $client->id);
|
||||
$this->createCredit($client);
|
||||
|
||||
$this->info('creating recurring invoice for client # ' . $client->id);
|
||||
$this->createRecurringInvoice($client);
|
||||
}
|
||||
|
||||
$this->createGateways($company, $user);
|
||||
|
||||
$this->createSubsData($company, $user);
|
||||
}
|
||||
|
||||
private function createSubsData($company, $user)
|
||||
{
|
||||
$gs = GroupSettingFactory::create($company->id, $user->id);
|
||||
$gs->name = "plans";
|
||||
$gs->save();
|
||||
|
||||
$p1 = Product::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
'product_key' => 'pro_plan',
|
||||
'notes' => 'The Pro Plan',
|
||||
'cost' => 10,
|
||||
'price' => 10,
|
||||
'quantity' => 1,
|
||||
]);
|
||||
|
||||
$p2 = Product::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
'product_key' => 'enterprise_plan',
|
||||
'notes' => 'The Enterprise Plan',
|
||||
'cost' => 14,
|
||||
'price' => 14,
|
||||
'quantity' => 1,
|
||||
]);
|
||||
|
||||
$p3 = Product::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
'product_key' => 'free_plan',
|
||||
'notes' => 'The Free Plan',
|
||||
'cost' => 0,
|
||||
'price' => 0,
|
||||
'quantity' => 1,
|
||||
]);
|
||||
|
||||
$webhook_config = [
|
||||
'post_purchase_url' => 'http://ninja.test:8000/api/admin/plan',
|
||||
'post_purchase_rest_method' => 'post',
|
||||
'post_purchase_headers' => [config('ninja.ninja_hosted_header') => config('ninja.ninja_hosted_secret')],
|
||||
];
|
||||
|
||||
$sub = SubscriptionFactory::create($company->id, $user->id);
|
||||
$sub->name = "Pro Plan";
|
||||
$sub->group_id = $gs->id;
|
||||
$sub->recurring_product_ids = "{$p1->hashed_id}";
|
||||
$sub->webhook_configuration = $webhook_config;
|
||||
$sub->allow_plan_changes = true;
|
||||
$sub->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
|
||||
$sub->save();
|
||||
|
||||
$sub = SubscriptionFactory::create($company->id, $user->id);
|
||||
$sub->name = "Enterprise Plan";
|
||||
$sub->group_id = $gs->id;
|
||||
$sub->recurring_product_ids = "{$p2->hashed_id}";
|
||||
$sub->webhook_configuration = $webhook_config;
|
||||
$sub->allow_plan_changes = true;
|
||||
$sub->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
|
||||
$sub->save();
|
||||
|
||||
$sub = SubscriptionFactory::create($company->id, $user->id);
|
||||
$sub->name = "Free Plan";
|
||||
$sub->group_id = $gs->id;
|
||||
$sub->recurring_product_ids = "{$p3->hashed_id}";
|
||||
$sub->webhook_configuration = $webhook_config;
|
||||
$sub->allow_plan_changes = true;
|
||||
$sub->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
|
||||
$sub->save();
|
||||
}
|
||||
|
||||
private function createClient($company, $user)
|
||||
{
|
||||
// dispatch(function () use ($company, $user) {
|
||||
|
||||
// });
|
||||
$client = Client::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
ClientContact::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'client_id' => $client->id,
|
||||
'company_id' => $company->id,
|
||||
'is_primary' => 1,
|
||||
'email' => 'user@example.com',
|
||||
]);
|
||||
|
||||
ClientContact::factory()->count(rand(1, 2))->create([
|
||||
'user_id' => $user->id,
|
||||
'client_id' => $client->id,
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
$client->number = $this->getNextClientNumber($client);
|
||||
|
||||
$settings = $client->settings;
|
||||
$settings->currency_id = "1";
|
||||
// $settings->use_credits_payment = "always";
|
||||
|
||||
$client->settings = $settings;
|
||||
|
||||
$country = Country::all()->random();
|
||||
|
||||
$client->country_id = $country->id;
|
||||
$client->save();
|
||||
}
|
||||
|
||||
private function createExpense($client)
|
||||
{
|
||||
Expense::factory()->count(rand(1, 20))->create([
|
||||
'user_id' => $client->user->id,
|
||||
'client_id' => $client->id,
|
||||
'company_id' => $client->company->id,
|
||||
]);
|
||||
}
|
||||
|
||||
private function createVendor($client)
|
||||
{
|
||||
$vendor = Vendor::factory()->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company->id,
|
||||
]);
|
||||
|
||||
VendorContact::factory()->create([
|
||||
'user_id' => $client->user->id,
|
||||
'vendor_id' => $vendor->id,
|
||||
'company_id' => $client->company->id,
|
||||
'is_primary' => 1,
|
||||
]);
|
||||
|
||||
VendorContact::factory()->count(rand(1, 2))->create([
|
||||
'user_id' => $client->user->id,
|
||||
'vendor_id' => $vendor->id,
|
||||
'company_id' => $client->company->id,
|
||||
'is_primary' => 0,
|
||||
]);
|
||||
}
|
||||
|
||||
private function createTask($client)
|
||||
{
|
||||
$vendor = Task::factory()->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company->id,
|
||||
]);
|
||||
}
|
||||
|
||||
private function createProject($client)
|
||||
{
|
||||
$vendor = Project::factory()->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company->id,
|
||||
'client_id' => $client->id,
|
||||
]);
|
||||
}
|
||||
|
||||
private function createInvoice($client)
|
||||
{
|
||||
$faker = Factory::create();
|
||||
|
||||
$invoice = InvoiceFactory::create($client->company->id, $client->user->id); //stub the company and user_id
|
||||
$invoice->client_id = $client->id;
|
||||
$dateable = Carbon::now()->subDays(rand(0, 90));
|
||||
$invoice->date = $dateable;
|
||||
|
||||
$invoice->line_items = $this->buildLineItems(rand(1, 10));
|
||||
$invoice->uses_inclusive_taxes = false;
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$invoice->tax_name1 = 'GST';
|
||||
$invoice->tax_rate1 = 10.00;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$invoice->tax_name2 = 'VAT';
|
||||
$invoice->tax_rate2 = 17.50;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$invoice->tax_name3 = 'CA Sales Tax';
|
||||
$invoice->tax_rate3 = 5;
|
||||
}
|
||||
|
||||
$invoice->custom_value1 = $faker->date;
|
||||
$invoice->custom_value2 = rand(0, 1) ? 'yes' : 'no';
|
||||
|
||||
$invoice->save();
|
||||
|
||||
$invoice_calc = new InvoiceSum($invoice);
|
||||
$invoice_calc->build();
|
||||
|
||||
$invoice = $invoice_calc->getInvoice();
|
||||
|
||||
if ($this->gateway === 'braintree') {
|
||||
$invoice->amount = 100; // Braintree sandbox only allows payments under 2,000 to complete successfully.
|
||||
}
|
||||
|
||||
$invoice->save();
|
||||
$invoice->service()->createInvitations()->markSent();
|
||||
|
||||
$this->invoice_repo->markSent($invoice);
|
||||
|
||||
event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars()));
|
||||
}
|
||||
|
||||
private function createCredit($client)
|
||||
{
|
||||
$faker = Factory::create();
|
||||
|
||||
$credit = Credit::factory()->create(['user_id' => $client->user->id, 'company_id' => $client->company->id, 'client_id' => $client->id]);
|
||||
|
||||
$dateable = Carbon::now()->subDays(rand(0, 90));
|
||||
$credit->date = $dateable;
|
||||
|
||||
$credit->line_items = $this->buildCreditItem();
|
||||
$credit->uses_inclusive_taxes = false;
|
||||
|
||||
$credit->save();
|
||||
|
||||
$invoice_calc = new InvoiceSum($credit);
|
||||
$invoice_calc->build();
|
||||
|
||||
$credit = $invoice_calc->getCredit();
|
||||
|
||||
$credit->save();
|
||||
$credit->service()->markSent()->save();
|
||||
$credit->service()->createInvitations();
|
||||
}
|
||||
|
||||
private function createQuote($client)
|
||||
{
|
||||
$faker = Factory::create();
|
||||
|
||||
$quote = Quote::factory()->create(['user_id' => $client->user->id, 'company_id' => $client->company->id, 'client_id' => $client->id]);
|
||||
$quote->date = $faker->date();
|
||||
$quote->client_id = $client->id;
|
||||
|
||||
$quote->setRelation('client', $client);
|
||||
|
||||
$quote->line_items = $this->buildLineItems(rand(1, 10));
|
||||
$quote->uses_inclusive_taxes = false;
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$quote->tax_name1 = 'GST';
|
||||
$quote->tax_rate1 = 10.00;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$quote->tax_name2 = 'VAT';
|
||||
$quote->tax_rate2 = 17.50;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$quote->tax_name3 = 'CA Sales Tax';
|
||||
$quote->tax_rate3 = 5;
|
||||
}
|
||||
|
||||
$quote->save();
|
||||
|
||||
$quote_calc = new InvoiceSum($quote);
|
||||
$quote_calc->build();
|
||||
|
||||
$quote = $quote_calc->getQuote();
|
||||
|
||||
$quote->save();
|
||||
|
||||
$quote->service()->markSent()->save();
|
||||
$quote->service()->createInvitations();
|
||||
}
|
||||
|
||||
|
||||
private function buildCreditItem()
|
||||
{
|
||||
$line_items = [];
|
||||
|
||||
$item = InvoiceItemFactory::create();
|
||||
$item->quantity = 1;
|
||||
$item->cost = 1000;
|
||||
|
||||
$product = Product::all()->random();
|
||||
|
||||
$item->cost = (float) $product->cost;
|
||||
$item->product_key = $product->product_key;
|
||||
$item->notes = $product->notes;
|
||||
$item->custom_value1 = $product->custom_value1;
|
||||
$item->custom_value2 = $product->custom_value2;
|
||||
$item->custom_value3 = $product->custom_value3;
|
||||
$item->custom_value4 = $product->custom_value4;
|
||||
|
||||
$line_items[] = $item;
|
||||
|
||||
|
||||
return $line_items;
|
||||
}
|
||||
|
||||
|
||||
private function buildLineItems($count = 1)
|
||||
{
|
||||
$line_items = [];
|
||||
|
||||
for ($x = 0; $x < $count; $x++) {
|
||||
$item = InvoiceItemFactory::create();
|
||||
$item->quantity = 1;
|
||||
//$item->cost = 10;
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$item->tax_name1 = 'GST';
|
||||
$item->tax_rate1 = 10.00;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$item->tax_name1 = 'VAT';
|
||||
$item->tax_rate1 = 17.50;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$item->tax_name1 = 'Sales Tax';
|
||||
$item->tax_rate1 = 5;
|
||||
}
|
||||
|
||||
$product = Product::all()->random();
|
||||
|
||||
$item->cost = (float) $product->cost;
|
||||
$item->product_key = $product->product_key;
|
||||
$item->notes = $product->notes;
|
||||
$item->custom_value1 = $product->custom_value1;
|
||||
$item->custom_value2 = $product->custom_value2;
|
||||
$item->custom_value3 = $product->custom_value3;
|
||||
$item->custom_value4 = $product->custom_value4;
|
||||
|
||||
$line_items[] = $item;
|
||||
}
|
||||
|
||||
return $line_items;
|
||||
}
|
||||
|
||||
private function warmCache()
|
||||
{
|
||||
/* Warm up the cache !*/
|
||||
$cached_tables = config('ninja.cached_tables');
|
||||
|
||||
foreach ($cached_tables as $name => $class) {
|
||||
// check that the table exists in case the migration is pending
|
||||
if (! Schema::hasTable((new $class())->getTable())) {
|
||||
continue;
|
||||
}
|
||||
if ($name == 'payment_terms') {
|
||||
$orderBy = 'num_days';
|
||||
} elseif ($name == 'fonts') {
|
||||
$orderBy = 'sort_order';
|
||||
} elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
|
||||
$orderBy = 'name';
|
||||
} else {
|
||||
$orderBy = 'id';
|
||||
}
|
||||
$tableData = $class::orderBy($orderBy)->get();
|
||||
if ($tableData->count()) {
|
||||
Cache::forever($name, $tableData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function createGateways($company, $user)
|
||||
{
|
||||
if (config('ninja.testvars.stripe') && ($this->gateway == 'all' || $this->gateway == 'stripe')) {
|
||||
$cg = new CompanyGateway;
|
||||
$cg->company_id = $company->id;
|
||||
$cg->user_id = $user->id;
|
||||
$cg->gateway_key = 'd14dd26a37cecc30fdd65700bfb55b23';
|
||||
$cg->require_cvv = true;
|
||||
$cg->require_billing_address = true;
|
||||
$cg->require_shipping_address = true;
|
||||
$cg->update_details = true;
|
||||
$cg->config = encrypt(config('ninja.testvars.stripe'));
|
||||
$cg->save();
|
||||
|
||||
$gateway_types = $cg->driver()->gatewayTypes();
|
||||
|
||||
$fees_and_limits = new stdClass;
|
||||
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
|
||||
|
||||
$cg->fees_and_limits = $fees_and_limits;
|
||||
$cg->save();
|
||||
}
|
||||
|
||||
if (config('ninja.testvars.paypal') && ($this->gateway == 'all' || $this->gateway == 'paypal')) {
|
||||
$cg = new CompanyGateway;
|
||||
$cg->company_id = $company->id;
|
||||
$cg->user_id = $user->id;
|
||||
$cg->gateway_key = '38f2c48af60c7dd69e04248cbb24c36e';
|
||||
$cg->require_cvv = true;
|
||||
$cg->require_billing_address = true;
|
||||
$cg->require_shipping_address = true;
|
||||
$cg->update_details = true;
|
||||
$cg->config = encrypt(config('ninja.testvars.paypal'));
|
||||
$cg->save();
|
||||
|
||||
$gateway_types = $cg->driver()->gatewayTypes();
|
||||
|
||||
$fees_and_limits = new stdClass;
|
||||
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
|
||||
|
||||
$cg->fees_and_limits = $fees_and_limits;
|
||||
$cg->save();
|
||||
}
|
||||
|
||||
if (config('ninja.testvars.checkout') && ($this->gateway == 'all' || $this->gateway == 'checkout')) {
|
||||
$cg = new CompanyGateway;
|
||||
$cg->company_id = $company->id;
|
||||
$cg->user_id = $user->id;
|
||||
$cg->gateway_key = '3758e7f7c6f4cecf0f4f348b9a00f456';
|
||||
$cg->require_cvv = true;
|
||||
$cg->require_billing_address = true;
|
||||
$cg->require_shipping_address = true;
|
||||
$cg->update_details = true;
|
||||
$cg->config = encrypt(config('ninja.testvars.checkout'));
|
||||
$cg->save();
|
||||
|
||||
$gateway_types = $cg->driver(new Client)->gatewayTypes();
|
||||
|
||||
$fees_and_limits = new stdClass;
|
||||
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
|
||||
|
||||
$cg->fees_and_limits = $fees_and_limits;
|
||||
$cg->save();
|
||||
}
|
||||
|
||||
if (config('ninja.testvars.authorize') && ($this->gateway == 'all' || $this->gateway == 'authorizenet')) {
|
||||
$cg = new CompanyGateway;
|
||||
$cg->company_id = $company->id;
|
||||
$cg->user_id = $user->id;
|
||||
$cg->gateway_key = '3b6621f970ab18887c4f6dca78d3f8bb';
|
||||
$cg->require_cvv = true;
|
||||
$cg->require_billing_address = true;
|
||||
$cg->require_shipping_address = true;
|
||||
$cg->update_details = true;
|
||||
$cg->config = encrypt(config('ninja.testvars.authorize'));
|
||||
$cg->save();
|
||||
|
||||
$gateway_types = $cg->driver(new Client)->gatewayTypes();
|
||||
|
||||
$fees_and_limits = new stdClass;
|
||||
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
|
||||
|
||||
$cg->fees_and_limits = $fees_and_limits;
|
||||
$cg->save();
|
||||
}
|
||||
|
||||
if (config('ninja.testvars.wepay') && ($this->gateway == 'all' || $this->gateway == 'wepay')) {
|
||||
$cg = new CompanyGateway;
|
||||
$cg->company_id = $company->id;
|
||||
$cg->user_id = $user->id;
|
||||
$cg->gateway_key = '8fdeed552015b3c7b44ed6c8ebd9e992';
|
||||
$cg->require_cvv = true;
|
||||
$cg->require_billing_address = true;
|
||||
$cg->require_shipping_address = true;
|
||||
$cg->update_details = true;
|
||||
$cg->config = encrypt(config('ninja.testvars.wepay'));
|
||||
$cg->save();
|
||||
|
||||
$gateway_types = $cg->driver()->gatewayTypes();
|
||||
|
||||
$fees_and_limits = new stdClass;
|
||||
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
|
||||
|
||||
$cg->fees_and_limits = $fees_and_limits;
|
||||
$cg->save();
|
||||
}
|
||||
|
||||
if (config('ninja.testvars.braintree') && ($this->gateway == 'all' || $this->gateway == 'braintree')) {
|
||||
$cg = new CompanyGateway;
|
||||
$cg->company_id = $company->id;
|
||||
$cg->user_id = $user->id;
|
||||
$cg->gateway_key = 'f7ec488676d310683fb51802d076d713';
|
||||
$cg->require_cvv = true;
|
||||
$cg->require_billing_address = true;
|
||||
$cg->require_shipping_address = true;
|
||||
$cg->update_details = true;
|
||||
$cg->config = encrypt(config('ninja.testvars.braintree'));
|
||||
$cg->save();
|
||||
|
||||
$gateway_types = $cg->driver()->gatewayTypes();
|
||||
|
||||
$fees_and_limits = new stdClass;
|
||||
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
|
||||
|
||||
$cg->fees_and_limits = $fees_and_limits;
|
||||
$cg->save();
|
||||
}
|
||||
|
||||
|
||||
if (config('ninja.testvars.paytrace.decrypted') && ($this->gateway == 'all' || $this->gateway == 'paytrace')) {
|
||||
$cg = new CompanyGateway;
|
||||
$cg->company_id = $company->id;
|
||||
$cg->user_id = $user->id;
|
||||
$cg->gateway_key = 'bbd736b3254b0aabed6ad7fda1298c88';
|
||||
$cg->require_cvv = true;
|
||||
$cg->require_billing_address = true;
|
||||
$cg->require_shipping_address = true;
|
||||
$cg->update_details = true;
|
||||
$cg->config = encrypt(config('ninja.testvars.paytrace.decrypted'));
|
||||
|
||||
$cg->save();
|
||||
|
||||
|
||||
$gateway_types = $cg->driver()->gatewayTypes();
|
||||
|
||||
$fees_and_limits = new stdClass;
|
||||
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
|
||||
|
||||
$cg->fees_and_limits = $fees_and_limits;
|
||||
$cg->save();
|
||||
}
|
||||
|
||||
if (config('ninja.testvars.mollie') && ($this->gateway == 'all' || $this->gateway == 'mollie')) {
|
||||
$cg = new CompanyGateway;
|
||||
$cg->company_id = $company->id;
|
||||
$cg->user_id = $user->id;
|
||||
$cg->gateway_key = '1bd651fb213ca0c9d66ae3c336dc77e8';
|
||||
$cg->require_cvv = true;
|
||||
$cg->require_billing_address = true;
|
||||
$cg->require_shipping_address = true;
|
||||
$cg->update_details = true;
|
||||
$cg->config = encrypt(config('ninja.testvars.mollie'));
|
||||
$cg->save();
|
||||
|
||||
$gateway_types = $cg->driver()->gatewayTypes();
|
||||
|
||||
$fees_and_limits = new stdClass;
|
||||
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
|
||||
|
||||
$cg->fees_and_limits = $fees_and_limits;
|
||||
$cg->save();
|
||||
}
|
||||
|
||||
if (config('ninja.testvars.square') && ($this->gateway == 'all' || $this->gateway == 'square')) {
|
||||
$cg = new CompanyGateway;
|
||||
$cg->company_id = $company->id;
|
||||
$cg->user_id = $user->id;
|
||||
$cg->gateway_key = '65faab2ab6e3223dbe848b1686490baz';
|
||||
$cg->require_cvv = true;
|
||||
$cg->require_billing_address = true;
|
||||
$cg->require_shipping_address = true;
|
||||
$cg->update_details = true;
|
||||
$cg->config = encrypt(config('ninja.testvars.square'));
|
||||
$cg->save();
|
||||
|
||||
$gateway_types = $cg->driver()->gatewayTypes();
|
||||
|
||||
$fees_and_limits = new stdClass;
|
||||
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
|
||||
|
||||
$cg->fees_and_limits = $fees_and_limits;
|
||||
$cg->save();
|
||||
}
|
||||
}
|
||||
|
||||
private function createRecurringInvoice($client)
|
||||
{
|
||||
$faker = Factory::create();
|
||||
|
||||
$invoice = RecurringInvoiceFactory::create($client->company->id, $client->user->id); //stub the company and user_id
|
||||
$invoice->client_id = $client->id;
|
||||
$dateable = Carbon::now()->subDays(rand(0, 90));
|
||||
$invoice->date = $dateable;
|
||||
|
||||
$invoice->line_items = $this->buildLineItems(rand(1, 10));
|
||||
$invoice->uses_inclusive_taxes = false;
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$invoice->tax_name1 = 'GST';
|
||||
$invoice->tax_rate1 = 10.00;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$invoice->tax_name2 = 'VAT';
|
||||
$invoice->tax_rate2 = 17.50;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$invoice->tax_name3 = 'CA Sales Tax';
|
||||
$invoice->tax_rate3 = 5;
|
||||
}
|
||||
|
||||
$invoice->custom_value1 = $faker->date;
|
||||
$invoice->custom_value2 = rand(0, 1) ? 'yes' : 'no';
|
||||
|
||||
$invoice->status_id = RecurringInvoice::STATUS_ACTIVE;
|
||||
$invoice->save();
|
||||
|
||||
$invoice_calc = new InvoiceSum($invoice);
|
||||
$invoice_calc->build();
|
||||
|
||||
$invoice = $invoice_calc->getInvoice();
|
||||
|
||||
$invoice->save();
|
||||
|
||||
event(new RecurringInvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars()));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,314 +1,700 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Ninja\Repositories\AccountRepository;
|
||||
use App\Ninja\Repositories\ClientRepository;
|
||||
use App\Ninja\Repositories\ExpenseRepository;
|
||||
use App\Ninja\Repositories\InvoiceRepository;
|
||||
use App\Ninja\Repositories\PaymentRepository;
|
||||
use App\Ninja\Repositories\VendorRepository;
|
||||
use App\Ninja\Repositories\TaskRepository;
|
||||
use App\Ninja\Repositories\ProjectRepository;
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Events\Invoice\InvoiceWasCreated;
|
||||
use App\Factory\InvoiceFactory;
|
||||
use App\Factory\InvoiceItemFactory;
|
||||
use App\Factory\QuoteFactory;
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use App\Models\TaxRate;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\Country;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Document;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Product;
|
||||
use App\Models\Project;
|
||||
use App\Models\ExpenseCategory;
|
||||
use Auth;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Task;
|
||||
use App\Models\User;
|
||||
use App\Models\Vendor;
|
||||
use App\Models\VendorContact;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Carbon\Carbon;
|
||||
use Faker\Factory;
|
||||
use Illuminate\Console\Command;
|
||||
use Utils;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class CreateTestData.
|
||||
*/
|
||||
class CreateTestData extends Command
|
||||
{
|
||||
use MakesHash, GeneratesCounter;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create Test Data';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:create-test-data {count=1} {create_account=false} {--database}';
|
||||
protected $signature = 'ninja:create-test-data {count=1}';
|
||||
|
||||
protected $invoice_repo;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
protected $token;
|
||||
|
||||
/**
|
||||
* CreateTestData constructor.
|
||||
* Execute the console command.
|
||||
*
|
||||
* @param ClientRepository $clientRepo
|
||||
* @param InvoiceRepository $invoiceRepo
|
||||
* @param PaymentRepository $paymentRepo
|
||||
* @param VendorRepository $vendorRepo
|
||||
* @param ExpenseRepository $expenseRepo
|
||||
* @param TaskRepository $taskRepo
|
||||
* @param AccountRepository $accountRepo
|
||||
*/
|
||||
public function __construct(
|
||||
ClientRepository $clientRepo,
|
||||
InvoiceRepository $invoiceRepo,
|
||||
PaymentRepository $paymentRepo,
|
||||
VendorRepository $vendorRepo,
|
||||
ExpenseRepository $expenseRepo,
|
||||
TaskRepository $taskRepo,
|
||||
ProjectRepository $projectRepo,
|
||||
AccountRepository $accountRepo)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->faker = Factory::create();
|
||||
|
||||
$this->clientRepo = $clientRepo;
|
||||
$this->invoiceRepo = $invoiceRepo;
|
||||
$this->paymentRepo = $paymentRepo;
|
||||
$this->vendorRepo = $vendorRepo;
|
||||
$this->expenseRepo = $expenseRepo;
|
||||
$this->taskRepo = $taskRepo;
|
||||
$this->projectRepo = $projectRepo;
|
||||
$this->accountRepo = $accountRepo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (Utils::isNinjaProd()) {
|
||||
$this->info('Unable to run in production');
|
||||
return false;
|
||||
if (config('ninja.is_docker')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $this->confirm('Are you sure you want to inject dummy data?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->invoice_repo = new InvoiceRepository();
|
||||
|
||||
$this->info(date('r').' Running CreateTestData...');
|
||||
$this->count = $this->argument('count');
|
||||
|
||||
if ($database = $this->option('database')) {
|
||||
config(['database.default' => $database]);
|
||||
}
|
||||
$this->info('Warming up cache');
|
||||
|
||||
if (filter_var($this->argument('create_account'), FILTER_VALIDATE_BOOLEAN)) {
|
||||
$this->info('Creating new account...');
|
||||
$account = $this->accountRepo->create(
|
||||
$this->faker->firstName,
|
||||
$this->faker->lastName,
|
||||
$this->faker->safeEmail
|
||||
);
|
||||
Auth::login($account->users[0]);
|
||||
} else {
|
||||
$this->info('Using first account...');
|
||||
Auth::loginUsingId(1);
|
||||
}
|
||||
$this->warmCache();
|
||||
|
||||
$this->createClients();
|
||||
$this->createVendors();
|
||||
$this->createOtherObjects();
|
||||
|
||||
$this->info('Done');
|
||||
$this->createSmallAccount();
|
||||
$this->createMediumAccount();
|
||||
$this->createLargeAccount();
|
||||
}
|
||||
|
||||
private function createClients()
|
||||
private function createSmallAccount()
|
||||
{
|
||||
for ($i = 0; $i < $this->count; $i++) {
|
||||
$data = [
|
||||
'name' => $this->faker->name,
|
||||
'address1' => $this->faker->streetAddress,
|
||||
'address2' => $this->faker->secondaryAddress,
|
||||
'city' => $this->faker->city,
|
||||
'state' => $this->faker->state,
|
||||
'postal_code' => $this->faker->postcode,
|
||||
'contacts' => [[
|
||||
'first_name' => $this->faker->firstName,
|
||||
'last_name' => $this->faker->lastName,
|
||||
'email' => $this->faker->safeEmail,
|
||||
'phone' => $this->faker->phoneNumber,
|
||||
]],
|
||||
];
|
||||
$this->info('Creating Small Account and Company');
|
||||
|
||||
$client = $this->clientRepo->save($data);
|
||||
$this->info('Client: ' . $client->name);
|
||||
$account = Account::factory()->create();
|
||||
$company = Company::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'slack_webhook_url' => config('ninja.notification.slack'),
|
||||
]);
|
||||
|
||||
$this->createInvoices($client);
|
||||
$this->createInvoices($client, true);
|
||||
// $this->createTasks($client);
|
||||
$account->default_company_id = $company->id;
|
||||
$account->save();
|
||||
|
||||
$user = User::whereEmail('small@example.com')->first();
|
||||
|
||||
if (! $user) {
|
||||
$user = User::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'email' => 'small@example.com',
|
||||
'confirmation_code' => $this->createDbHash(config('database.default')),
|
||||
]);
|
||||
}
|
||||
|
||||
$company_token = new CompanyToken;
|
||||
$company_token->user_id = $user->id;
|
||||
$company_token->company_id = $company->id;
|
||||
$company_token->account_id = $account->id;
|
||||
$company_token->name = 'test token';
|
||||
$company_token->token = Str::random(64);
|
||||
$company_token->is_system = true;
|
||||
|
||||
$company_token->save();
|
||||
|
||||
$user->companies()->attach($company->id, [
|
||||
'account_id' => $account->id,
|
||||
'is_owner' => 1,
|
||||
'is_admin' => 1,
|
||||
'is_locked' => 0,
|
||||
'notifications' => CompanySettings::notificationDefaults(),
|
||||
// 'permissions' => '',
|
||||
'settings' => null,
|
||||
]);
|
||||
|
||||
Product::factory()->count(50)->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
$this->info('Creating '.$this->count.' clients');
|
||||
|
||||
for ($x = 0; $x < $this->count; $x++) {
|
||||
$z = $x + 1;
|
||||
$this->info('Creating client # '.$z);
|
||||
|
||||
$this->createClient($company, $user);
|
||||
}
|
||||
|
||||
for ($x = 0; $x < $this->count; $x++) {
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating invoice for client #'.$client->id);
|
||||
$this->createInvoice($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating credit for client #'.$client->id);
|
||||
$this->createCredit($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating quote for client #'.$client->id);
|
||||
$this->createQuote($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating expense for client #'.$client->id);
|
||||
$this->createExpense($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating vendor for client #'.$client->id);
|
||||
$this->createVendor($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating task for client #'.$client->id);
|
||||
$this->createTask($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating project for client #'.$client->id);
|
||||
$this->createProject($client);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $client
|
||||
*/
|
||||
private function createInvoices($client, $isQuote = false)
|
||||
private function createMediumAccount()
|
||||
{
|
||||
for ($i = 0; $i < $this->count; $i++) {
|
||||
$data = [
|
||||
'is_public' => true,
|
||||
'is_quote' => $isQuote,
|
||||
'client_id' => $client->id,
|
||||
'invoice_date_sql' => date_create()->modify(rand(-100, 100) . ' days')->format('Y-m-d'),
|
||||
'due_date_sql' => date_create()->modify(rand(-100, 100) . ' days')->format('Y-m-d'),
|
||||
'invoice_items' => [[
|
||||
'product_key' => $this->faker->word,
|
||||
'qty' => $this->faker->randomDigit + 1,
|
||||
'cost' => $this->faker->randomFloat(2, 1, 10),
|
||||
'notes' => $this->faker->text($this->faker->numberBetween(50, 300)),
|
||||
]],
|
||||
];
|
||||
$this->info('Creating Medium Account and Company');
|
||||
|
||||
$invoice = $this->invoiceRepo->save($data);
|
||||
$this->info('Invoice: ' . $invoice->invoice_number);
|
||||
$account = Account::factory()->create();
|
||||
$company = Company::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'slack_webhook_url' => config('ninja.notification.slack'),
|
||||
]);
|
||||
|
||||
if (! $isQuote) {
|
||||
$this->createPayment($client, $invoice);
|
||||
$account->default_company_id = $company->id;
|
||||
$account->save();
|
||||
|
||||
$user = User::whereEmail('medium@example.com')->first();
|
||||
|
||||
if (! $user) {
|
||||
$user = User::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'email' => 'medium@example.com',
|
||||
'confirmation_code' => $this->createDbHash(config('database.default')),
|
||||
]);
|
||||
}
|
||||
|
||||
$company_token = new CompanyToken;
|
||||
$company_token->user_id = $user->id;
|
||||
$company_token->company_id = $company->id;
|
||||
$company_token->account_id = $account->id;
|
||||
$company_token->name = 'test token';
|
||||
$company_token->token = Str::random(64);
|
||||
$company_token->is_system = true;
|
||||
$company_token->save();
|
||||
|
||||
$user->companies()->attach($company->id, [
|
||||
'account_id' => $account->id,
|
||||
'is_owner' => 1,
|
||||
'is_admin' => 1,
|
||||
'is_locked' => 0,
|
||||
'notifications' => CompanySettings::notificationDefaults(),
|
||||
// 'permissions' => '',
|
||||
'settings' => null,
|
||||
]);
|
||||
|
||||
Product::factory()->count(50)->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
$this->count = $this->count * 10;
|
||||
|
||||
$this->info('Creating '.$this->count.' clients');
|
||||
|
||||
for ($x = 0; $x < $this->count; $x++) {
|
||||
$z = $x + 1;
|
||||
$this->info('Creating client # '.$z);
|
||||
|
||||
$this->createClient($company, $user);
|
||||
}
|
||||
|
||||
for ($x = 0; $x < $this->count * 100; $x++) {
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating invoice for client #'.$client->id);
|
||||
$this->createInvoice($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating credit for client #'.$client->id);
|
||||
$this->createCredit($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating quote for client #'.$client->id);
|
||||
$this->createQuote($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating expense for client #'.$client->id);
|
||||
$this->createExpense($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating vendor for client #'.$client->id);
|
||||
$this->createVendor($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating task for client #'.$client->id);
|
||||
$this->createTask($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating project for client #'.$client->id);
|
||||
$this->createProject($client);
|
||||
}
|
||||
}
|
||||
|
||||
private function createLargeAccount()
|
||||
{
|
||||
$this->info('Creating Large Account and Company');
|
||||
|
||||
$account = Account::factory()->create();
|
||||
$company = Company::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'slack_webhook_url' => config('ninja.notification.slack'),
|
||||
'is_large' => true,
|
||||
]);
|
||||
|
||||
$account->default_company_id = $company->id;
|
||||
$account->save();
|
||||
|
||||
$user = User::whereEmail('large@example.com')->first();
|
||||
|
||||
if (! $user) {
|
||||
$user = User::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'email' => 'large@example.com',
|
||||
'confirmation_code' => $this->createDbHash(config('database.default')),
|
||||
]);
|
||||
}
|
||||
|
||||
$company_token = new CompanyToken;
|
||||
$company_token->user_id = $user->id;
|
||||
$company_token->company_id = $company->id;
|
||||
$company_token->account_id = $account->id;
|
||||
$company_token->name = 'test token';
|
||||
$company_token->token = Str::random(64);
|
||||
$company_token->is_system = true;
|
||||
$company_token->save();
|
||||
|
||||
$user->companies()->attach($company->id, [
|
||||
'account_id' => $account->id,
|
||||
'is_owner' => 1,
|
||||
'is_admin' => 1,
|
||||
'is_locked' => 0,
|
||||
'notifications' => CompanySettings::notificationDefaults(),
|
||||
// 'permissions' => '',
|
||||
'settings' => null,
|
||||
]);
|
||||
|
||||
Product::factory()->count(15000)->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
$this->count = $this->count * 10;
|
||||
|
||||
$this->info('Creating '.$this->count.' clients');
|
||||
|
||||
for ($x = 0; $x < $this->count * 100; $x++) {
|
||||
$z = $x + 1;
|
||||
$this->info('Creating client # '.$z);
|
||||
|
||||
$this->createClient($company, $user);
|
||||
}
|
||||
|
||||
for ($x = 0; $x < $this->count; $x++) {
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating invoice for client #'.$client->id);
|
||||
$this->createInvoice($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating credit for client #'.$client->id);
|
||||
$this->createCredit($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating quote for client #'.$client->id);
|
||||
$this->createQuote($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating expense for client #'.$client->id);
|
||||
$this->createExpense($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating vendor for client #'.$client->id);
|
||||
$this->createVendor($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating task for client #'.$client->id);
|
||||
$this->createTask($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating project for client #'.$client->id);
|
||||
$this->createProject($client);
|
||||
}
|
||||
}
|
||||
|
||||
private function createClient($company, $user)
|
||||
{
|
||||
// dispatch(function () use ($company, $user) {
|
||||
|
||||
// });
|
||||
$client = Client::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
Document::factory()->count(50)->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
'documentable_type' => Client::class,
|
||||
'documentable_id' => $client->id,
|
||||
]);
|
||||
|
||||
ClientContact::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'client_id' => $client->id,
|
||||
'company_id' => $company->id,
|
||||
'is_primary' => 1,
|
||||
]);
|
||||
|
||||
ClientContact::factory()->count(rand(1, 5))->create([
|
||||
'user_id' => $user->id,
|
||||
'client_id' => $client->id,
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
$client->number = $this->getNextClientNumber($client);
|
||||
|
||||
$settings = $client->settings;
|
||||
$settings->currency_id = (string) rand(1, 79);
|
||||
$client->settings = $settings;
|
||||
|
||||
$country = Country::all()->random();
|
||||
|
||||
$client->country_id = $country->id;
|
||||
$client->save();
|
||||
}
|
||||
|
||||
private function createExpense($client)
|
||||
{
|
||||
Expense::factory()->count(rand(1, 5))->create([
|
||||
'user_id' => $client->user->id,
|
||||
'client_id' => $client->id,
|
||||
'company_id' => $client->company->id,
|
||||
]);
|
||||
}
|
||||
|
||||
private function createVendor($client)
|
||||
{
|
||||
$vendor = Vendor::factory()->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company->id,
|
||||
]);
|
||||
|
||||
Document::factory()->count(1)->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company_id,
|
||||
'documentable_type' => Vendor::class,
|
||||
'documentable_id' => $vendor->id,
|
||||
]);
|
||||
|
||||
VendorContact::factory()->create([
|
||||
'user_id' => $client->user->id,
|
||||
'vendor_id' => $vendor->id,
|
||||
'company_id' => $client->company->id,
|
||||
'is_primary' => 1,
|
||||
]);
|
||||
|
||||
VendorContact::factory()->count(rand(1, 5))->create([
|
||||
'user_id' => $client->user->id,
|
||||
'vendor_id' => $vendor->id,
|
||||
'company_id' => $client->company->id,
|
||||
'is_primary' => 0,
|
||||
]);
|
||||
}
|
||||
|
||||
private function createTask($client)
|
||||
{
|
||||
$vendor = Task::factory()->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company->id,
|
||||
]);
|
||||
|
||||
Document::factory()->count(5)->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company_id,
|
||||
'documentable_type' => Task::class,
|
||||
'documentable_id' => $vendor->id,
|
||||
]);
|
||||
}
|
||||
|
||||
private function createProject($client)
|
||||
{
|
||||
$vendor = Project::factory()->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company->id,
|
||||
]);
|
||||
|
||||
Document::factory()->count(5)->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company_id,
|
||||
'documentable_type' => Project::class,
|
||||
'documentable_id' => $vendor->id,
|
||||
]);
|
||||
}
|
||||
|
||||
private function createInvoice($client)
|
||||
{
|
||||
$faker = Factory::create();
|
||||
|
||||
$invoice = InvoiceFactory::create($client->company->id, $client->user->id); //stub the company and user_id
|
||||
$invoice->client_id = $client->id;
|
||||
// $invoice->date = $faker->date();
|
||||
$dateable = Carbon::now()->subDays(rand(0, 90));
|
||||
$invoice->date = $dateable;
|
||||
|
||||
$invoice->line_items = $this->buildLineItems(rand(1, 10));
|
||||
$invoice->uses_inclusive_taxes = false;
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$invoice->tax_name1 = 'GST';
|
||||
$invoice->tax_rate1 = 10.00;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$invoice->tax_name2 = 'VAT';
|
||||
$invoice->tax_rate2 = 17.50;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$invoice->tax_name3 = 'CA Sales Tax';
|
||||
$invoice->tax_rate3 = 5;
|
||||
}
|
||||
|
||||
$invoice->custom_value1 = $faker->date();
|
||||
$invoice->custom_value2 = rand(0, 1) ? 'yes' : 'no';
|
||||
|
||||
$invoice->save();
|
||||
|
||||
$invoice_calc = new InvoiceSum($invoice);
|
||||
$invoice_calc->build();
|
||||
|
||||
$invoice = $invoice_calc->getInvoice();
|
||||
|
||||
$invoice->save();
|
||||
$invoice->service()->createInvitations()->markSent();
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$this->invoice_repo->markSent($invoice);
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$invoice = $invoice->service()->markPaid()->save();
|
||||
}
|
||||
|
||||
Document::factory()->count(5)->create([
|
||||
'user_id' => $invoice->user->id,
|
||||
'company_id' => $invoice->company_id,
|
||||
'documentable_type' => Invoice::class,
|
||||
'documentable_id' => $invoice->id,
|
||||
]);
|
||||
|
||||
RecurringInvoice::factory()->create(['user_id' => $invoice->user->id, 'company_id' => $invoice->company->id, 'client_id' => $invoice->client_id]);
|
||||
|
||||
event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars()));
|
||||
}
|
||||
|
||||
private function createCredit($client)
|
||||
{
|
||||
$faker = Factory::create();
|
||||
|
||||
$credit = Credit::factory()->create(['user_id' => $client->user->id, 'company_id' => $client->company->id, 'client_id' => $client->id]);
|
||||
|
||||
$dateable = Carbon::now()->subDays(rand(0, 90));
|
||||
$credit->date = $dateable;
|
||||
|
||||
$credit->line_items = $this->buildLineItems(rand(1, 10));
|
||||
$credit->uses_inclusive_taxes = false;
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$credit->tax_name1 = 'GST';
|
||||
$credit->tax_rate1 = 10.00;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$credit->tax_name2 = 'VAT';
|
||||
$credit->tax_rate2 = 17.50;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$credit->tax_name3 = 'CA Sales Tax';
|
||||
$credit->tax_rate3 = 5;
|
||||
}
|
||||
|
||||
$credit->save();
|
||||
|
||||
$invoice_calc = new InvoiceSum($credit);
|
||||
$invoice_calc->build();
|
||||
|
||||
$credit = $invoice_calc->getCredit();
|
||||
|
||||
$credit->save();
|
||||
$credit->service()->markSent()->save();
|
||||
$credit->service()->createInvitations();
|
||||
}
|
||||
|
||||
private function createQuote($client)
|
||||
{
|
||||
$faker = Factory::create();
|
||||
|
||||
//$quote = QuoteFactory::create($client->company->id, $client->user->id);//stub the company and user_id
|
||||
$quote = Quote::factory()->create(['user_id' => $client->user->id, 'company_id' => $client->company->id, 'client_id' => $client->id]);
|
||||
$quote->date = $faker->date();
|
||||
$quote->client_id = $client->id;
|
||||
|
||||
$quote->setRelation('client', $client);
|
||||
|
||||
$quote->line_items = $this->buildLineItems(rand(1, 10));
|
||||
$quote->uses_inclusive_taxes = false;
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$quote->tax_name1 = 'GST';
|
||||
$quote->tax_rate1 = 10.00;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$quote->tax_name2 = 'VAT';
|
||||
$quote->tax_rate2 = 17.50;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$quote->tax_name3 = 'CA Sales Tax';
|
||||
$quote->tax_rate3 = 5;
|
||||
}
|
||||
|
||||
$quote->save();
|
||||
|
||||
$quote_calc = new InvoiceSum($quote);
|
||||
$quote_calc->build();
|
||||
|
||||
$quote = $quote_calc->getQuote();
|
||||
|
||||
$quote->save();
|
||||
|
||||
$quote->service()->markSent()->save();
|
||||
$quote->service()->createInvitations();
|
||||
}
|
||||
|
||||
private function buildLineItems($count = 1)
|
||||
{
|
||||
$line_items = [];
|
||||
|
||||
for ($x = 0; $x < $count; $x++) {
|
||||
$item = InvoiceItemFactory::create();
|
||||
$item->quantity = 1;
|
||||
//$item->cost = 10;
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$item->tax_name1 = 'GST';
|
||||
$item->tax_rate1 = 10.00;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$item->tax_name1 = 'VAT';
|
||||
$item->tax_rate1 = 17.50;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$item->tax_name1 = 'Sales Tax';
|
||||
$item->tax_rate1 = 5;
|
||||
}
|
||||
|
||||
$product = Product::all()->random();
|
||||
|
||||
$item->cost = (float) $product->cost;
|
||||
$item->product_key = $product->product_key;
|
||||
$item->notes = $product->notes;
|
||||
$item->custom_value1 = $product->custom_value1;
|
||||
$item->custom_value2 = $product->custom_value2;
|
||||
$item->custom_value3 = $product->custom_value3;
|
||||
$item->custom_value4 = $product->custom_value4;
|
||||
|
||||
$line_items[] = $item;
|
||||
}
|
||||
|
||||
return $line_items;
|
||||
}
|
||||
|
||||
private function warmCache()
|
||||
{
|
||||
/* Warm up the cache !*/
|
||||
$cached_tables = config('ninja.cached_tables');
|
||||
|
||||
foreach ($cached_tables as $name => $class) {
|
||||
if (! Cache::has($name)) {
|
||||
// check that the table exists in case the migration is pending
|
||||
if (! Schema::hasTable((new $class())->getTable())) {
|
||||
continue;
|
||||
}
|
||||
if ($name == 'payment_terms') {
|
||||
$orderBy = 'num_days';
|
||||
} elseif ($name == 'fonts') {
|
||||
$orderBy = 'sort_order';
|
||||
} elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
|
||||
$orderBy = 'name';
|
||||
} else {
|
||||
$orderBy = 'id';
|
||||
}
|
||||
$tableData = $class::orderBy($orderBy)->get();
|
||||
if ($tableData->count()) {
|
||||
Cache::forever($name, $tableData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $client
|
||||
* @param $invoice
|
||||
*/
|
||||
private function createPayment($client, $invoice)
|
||||
{
|
||||
$data = [
|
||||
'invoice_id' => $invoice->id,
|
||||
'client_id' => $client->id,
|
||||
'amount' => $this->faker->randomFloat(2, 0, $invoice->amount),
|
||||
'payment_date_sql' => date_create()->modify(rand(-100, 100) . ' days')->format('Y-m-d'),
|
||||
];
|
||||
|
||||
$payment = $this->paymentRepo->save($data);
|
||||
|
||||
$this->info('Payment: ' . $payment->amount);
|
||||
}
|
||||
|
||||
private function createTasks($client)
|
||||
{
|
||||
$data = [
|
||||
'client_id' => $client->id,
|
||||
'name' => $this->faker->sentence(3),
|
||||
];
|
||||
$project = $this->projectRepo->save($data);
|
||||
|
||||
for ($i = 0; $i < $this->count; $i++) {
|
||||
$startTime = date_create()->modify(rand(-100, 100) . ' days')->format('U');
|
||||
$endTime = $startTime + (60 * 60 * 2);
|
||||
$timeLog = "[[{$startTime},{$endTime}]]";
|
||||
$data = [
|
||||
'client_id' => $client->id,
|
||||
'project_id' => $project->id,
|
||||
'description' => $this->faker->text($this->faker->numberBetween(50, 300)),
|
||||
'time_log' => $timeLog,
|
||||
];
|
||||
|
||||
$this->taskRepo->save(false, $data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function createVendors()
|
||||
{
|
||||
for ($i = 0; $i < $this->count; $i++) {
|
||||
$data = [
|
||||
'name' => $this->faker->name,
|
||||
'address1' => $this->faker->streetAddress,
|
||||
'address2' => $this->faker->secondaryAddress,
|
||||
'city' => $this->faker->city,
|
||||
'state' => $this->faker->state,
|
||||
'postal_code' => $this->faker->postcode,
|
||||
'vendor_contacts' => [[
|
||||
'first_name' => $this->faker->firstName,
|
||||
'last_name' => $this->faker->lastName,
|
||||
'email' => $this->faker->safeEmail,
|
||||
'phone' => $this->faker->phoneNumber,
|
||||
]],
|
||||
];
|
||||
|
||||
$vendor = $this->vendorRepo->save($data);
|
||||
$this->info('Vendor: ' . $vendor->name);
|
||||
|
||||
$this->createExpense($vendor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $vendor
|
||||
*/
|
||||
private function createExpense($vendor)
|
||||
{
|
||||
for ($i = 0; $i < $this->count; $i++) {
|
||||
$data = [
|
||||
'vendor_id' => $vendor->id,
|
||||
'amount' => $this->faker->randomFloat(2, 1, 10),
|
||||
'expense_date' => date_create()->modify(rand(-100, 100) . ' days')->format('Y-m-d'),
|
||||
'public_notes' => '',
|
||||
];
|
||||
|
||||
$expense = $this->expenseRepo->save($data);
|
||||
$this->info('Expense: ' . $expense->amount);
|
||||
}
|
||||
}
|
||||
|
||||
private function createOtherObjects()
|
||||
{
|
||||
$this->createTaxRate('Tax 1', 10, 1);
|
||||
$this->createTaxRate('Tax 2', 20, 2);
|
||||
|
||||
$this->createCategory('Category 1', 1);
|
||||
$this->createCategory('Category 1', 2);
|
||||
|
||||
$this->createProject('Project 1', 1);
|
||||
$this->createProject('Project 2', 2);
|
||||
}
|
||||
|
||||
private function createTaxRate($name, $rate, $publicId)
|
||||
{
|
||||
$taxRate = new TaxRate();
|
||||
$taxRate->name = $name;
|
||||
$taxRate->rate = $rate;
|
||||
$taxRate->account_id = 1;
|
||||
$taxRate->user_id = 1;
|
||||
$taxRate->public_id = $publicId;
|
||||
$taxRate->save();
|
||||
}
|
||||
|
||||
private function createCategory($name, $publicId)
|
||||
{
|
||||
$category = new ExpenseCategory();
|
||||
$category->name = $name;
|
||||
$category->account_id = 1;
|
||||
$category->user_id = 1;
|
||||
$category->public_id = $publicId;
|
||||
$category->save();
|
||||
}
|
||||
|
||||
private function createProject($name, $publicId)
|
||||
{
|
||||
$project = new Project();
|
||||
$project->name = $name;
|
||||
$project->account_id = 1;
|
||||
$project->client_id = 1;
|
||||
$project->user_id = 1;
|
||||
$project->public_id = $publicId;
|
||||
$project->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getArguments()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
648
app/Console/Commands/DemoMode.php
Normal file
648
app/Console/Commands/DemoMode.php
Normal file
|
|
@ -0,0 +1,648 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Events\Invoice\InvoiceWasCreated;
|
||||
use App\Factory\InvoiceFactory;
|
||||
use App\Factory\InvoiceItemFactory;
|
||||
use App\Factory\RecurringInvoiceFactory;
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use App\Jobs\Company\CreateCompanyPaymentTerms;
|
||||
use App\Jobs\Company\CreateCompanyTaskStatuses;
|
||||
use App\Jobs\Ninja\CompanySizeCheck;
|
||||
use App\Jobs\Util\VersionCheck;
|
||||
use App\Models\Account;
|
||||
use App\Models\BankIntegration;
|
||||
use App\Models\BankTransaction;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Product;
|
||||
use App\Models\Project;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Task;
|
||||
use App\Models\TaskStatus;
|
||||
use App\Models\User;
|
||||
use App\Models\Vendor;
|
||||
use App\Models\VendorContact;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\AppSetup;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class DemoMode extends Command
|
||||
{
|
||||
use MakesHash, GeneratesCounter, AppSetup;
|
||||
|
||||
protected $signature = 'ninja:demo-mode';
|
||||
|
||||
protected $description = 'Setup demo mode';
|
||||
|
||||
protected $invoice_repo;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
set_time_limit(0);
|
||||
|
||||
if (config('ninja.is_docker')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->invoice_repo = new InvoiceRepository();
|
||||
|
||||
$cached_tables = config('ninja.cached_tables');
|
||||
|
||||
$this->info('Migrating');
|
||||
Artisan::call('migrate:fresh --force');
|
||||
|
||||
$this->info('Seeding');
|
||||
Artisan::call('db:seed --force');
|
||||
|
||||
$this->buildCache(true);
|
||||
|
||||
$this->info('Seeding Random Data');
|
||||
$this->createSmallAccount();
|
||||
|
||||
(new VersionCheck())->handle();
|
||||
|
||||
(new CompanySizeCheck())->handle();
|
||||
}
|
||||
|
||||
private function createSmallAccount()
|
||||
{
|
||||
$faker = \Faker\Factory::create();
|
||||
|
||||
$this->count = 25;
|
||||
|
||||
$this->info('Creating Small Account and Company');
|
||||
|
||||
$account = Account::factory()->create();
|
||||
$company = Company::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'slack_webhook_url' => config('ninja.notification.slack'),
|
||||
'enabled_modules' => 32767,
|
||||
'company_key' => 'KEY',
|
||||
'enable_shop_api' => true,
|
||||
'markdown_email_enabled' => true,
|
||||
'markdown_enabled' => false,
|
||||
]);
|
||||
|
||||
$settings = $company->settings;
|
||||
|
||||
$settings->name = $faker->company();
|
||||
$settings->address1 = $faker->buildingNumber();
|
||||
$settings->address2 = $faker->streetAddress();
|
||||
$settings->city = $faker->city();
|
||||
$settings->state = $faker->state();
|
||||
$settings->postal_code = $faker->postcode();
|
||||
$settings->website = $faker->url();
|
||||
$settings->vat_number = (string) $faker->numberBetween(123456789, 987654321);
|
||||
$settings->phone = (string) $faker->phoneNumber();
|
||||
|
||||
$company->settings = $settings;
|
||||
$company->save();
|
||||
|
||||
$account->default_company_id = $company->id;
|
||||
$account->save();
|
||||
|
||||
$user = User::whereEmail('small@example.com')->first();
|
||||
|
||||
if (! $user) {
|
||||
$user = User::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'email' => 'small@example.com',
|
||||
'confirmation_code' => $this->createDbHash(config('database.default')),
|
||||
'email_verified_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
(new CreateCompanyPaymentTerms($company, $user))->handle();
|
||||
(new CreateCompanyTaskStatuses($company, $user))->handle();
|
||||
|
||||
$company_token = new CompanyToken;
|
||||
$company_token->user_id = $user->id;
|
||||
$company_token->company_id = $company->id;
|
||||
$company_token->account_id = $account->id;
|
||||
$company_token->name = 'test token';
|
||||
$company_token->token = Str::random(64);
|
||||
$company_token->is_system = true;
|
||||
|
||||
$company_token->save();
|
||||
|
||||
$user->companies()->attach($company->id, [
|
||||
'account_id' => $account->id,
|
||||
'is_owner' => 1,
|
||||
'is_admin' => 1,
|
||||
'is_locked' => 0,
|
||||
'notifications' => CompanySettings::notificationDefaults(),
|
||||
// 'permissions' => '',
|
||||
'settings' => null,
|
||||
]);
|
||||
|
||||
$u2 = User::where('email', 'demo@invoiceninja.com')->first();
|
||||
|
||||
if (! $u2) {
|
||||
$u2 = User::factory()->create([
|
||||
'email' => 'demo@invoiceninja.com',
|
||||
'password' => Hash::make('Password0'),
|
||||
'account_id' => $account->id,
|
||||
'confirmation_code' => $this->createDbHash(config('database.default')),
|
||||
'email_verified_at' => now(),
|
||||
]);
|
||||
|
||||
$company_token = new CompanyToken;
|
||||
$company_token->user_id = $u2->id;
|
||||
$company_token->company_id = $company->id;
|
||||
$company_token->account_id = $account->id;
|
||||
$company_token->name = 'test token';
|
||||
$company_token->token = 'TOKEN';
|
||||
$company_token->save();
|
||||
|
||||
$u2->companies()->attach($company->id, [
|
||||
'account_id' => $account->id,
|
||||
'is_owner' => 1,
|
||||
'is_admin' => 1,
|
||||
'is_locked' => 0,
|
||||
'notifications' => CompanySettings::notificationDefaults(),
|
||||
'permissions' => '',
|
||||
'settings' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
Product::factory()->count(50)->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
$bi = BankIntegration::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'company_id' => $company->id,
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
|
||||
BankTransaction::factory()->count(50)->create([
|
||||
'bank_integration_id' => $bi->id,
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
$this->info('Creating '.$this->count.' clients');
|
||||
|
||||
for ($x = 0; $x < $this->count; $x++) {
|
||||
$z = $x + 1;
|
||||
$this->info('Creating client # '.$z);
|
||||
|
||||
$this->createClient($company, $user, $u2->id);
|
||||
}
|
||||
|
||||
for ($x = 0; $x < $this->count; $x++) {
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating entities for client #'.$client->id);
|
||||
$this->createInvoice($client, $u2->id);
|
||||
|
||||
$this->createRecurringInvoice($client, $u2->id);
|
||||
|
||||
$client = $company->clients->random();
|
||||
$this->createCredit($client, $u2->id);
|
||||
|
||||
$client = $company->clients->random();
|
||||
$this->createQuote($client, $u2->id);
|
||||
|
||||
$client = $company->clients->random();
|
||||
$this->createExpense($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
$this->createVendor($client, $u2->id);
|
||||
|
||||
$client = $company->clients->random();
|
||||
$this->createTask($client, $u2->id);
|
||||
|
||||
$client = $company->clients->random();
|
||||
$this->createProject($client, $u2->id);
|
||||
}
|
||||
}
|
||||
|
||||
private function createClient($company, $user, $assigned_user_id = null)
|
||||
{
|
||||
// dispatch(function () use ($company, $user) {
|
||||
|
||||
// });
|
||||
$client = Client::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
ClientContact::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'client_id' => $client->id,
|
||||
'company_id' => $company->id,
|
||||
'is_primary' => 1,
|
||||
]);
|
||||
|
||||
ClientContact::factory()->count(rand(1, 5))->create([
|
||||
'user_id' => $user->id,
|
||||
'client_id' => $client->id,
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
$client->number = $this->getNextClientNumber($client);
|
||||
|
||||
$settings = $client->settings;
|
||||
$settings->currency_id = (string) rand(1, 3);
|
||||
$client->settings = $settings;
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$client->assigned_user_id = $assigned_user_id;
|
||||
}
|
||||
|
||||
$client->country_id = array_rand([36, 392, 840, 124, 276, 826]);
|
||||
$client->save();
|
||||
}
|
||||
|
||||
private function createExpense($client)
|
||||
{
|
||||
Expense::factory()->count(rand(1, 5))->create([
|
||||
'user_id' => $client->user_id,
|
||||
'client_id' => $client->id,
|
||||
'company_id' => $client->company_id,
|
||||
]);
|
||||
|
||||
Expense::all()->each(function ($expense) {
|
||||
$expense->number = $this->getNextExpenseNumber($expense);
|
||||
$expense->save();
|
||||
});
|
||||
}
|
||||
|
||||
private function createVendor($client, $assigned_user_id = null)
|
||||
{
|
||||
$vendor = Vendor::factory()->create([
|
||||
'user_id' => $client->user_id,
|
||||
'company_id' => $client->company_id,
|
||||
]);
|
||||
|
||||
VendorContact::factory()->create([
|
||||
'user_id' => $client->user->id,
|
||||
'vendor_id' => $vendor->id,
|
||||
'company_id' => $client->company_id,
|
||||
'is_primary' => 1,
|
||||
]);
|
||||
|
||||
VendorContact::factory()->count(rand(1, 5))->create([
|
||||
'user_id' => $client->user->id,
|
||||
'vendor_id' => $vendor->id,
|
||||
'company_id' => $client->company_id,
|
||||
'is_primary' => 0,
|
||||
]);
|
||||
|
||||
$vendor->number = $this->getNextVendorNumber($vendor);
|
||||
$vendor->save();
|
||||
}
|
||||
|
||||
private function createTask($client, $assigned_user_id = null)
|
||||
{
|
||||
$task = Task::factory()->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company_id,
|
||||
'client_id' => $client->id,
|
||||
]);
|
||||
|
||||
$task->status_id = TaskStatus::all()->random()->id;
|
||||
|
||||
$task->number = $this->getNextTaskNumber($task);
|
||||
$task->save();
|
||||
}
|
||||
|
||||
private function createProject($client, $assigned_user_id = null)
|
||||
{
|
||||
$project = Project::factory()->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company_id,
|
||||
'client_id' => $client->id,
|
||||
]);
|
||||
|
||||
$project->number = $this->getNextProjectNumber($project);
|
||||
$project->save();
|
||||
}
|
||||
|
||||
private function createInvoice($client, $assigned_user_id = null)
|
||||
{
|
||||
$faker = \Faker\Factory::create();
|
||||
|
||||
$invoice = InvoiceFactory::create($client->company->id, $client->user->id); //stub the company and user_id
|
||||
$invoice->client_id = $client->id;
|
||||
|
||||
if ((bool) rand(0, 1)) {
|
||||
$dateable = Carbon::now()->subDays(rand(0, 90));
|
||||
} else {
|
||||
$dateable = Carbon::now()->addDays(rand(0, 90));
|
||||
}
|
||||
|
||||
$invoice->date = $dateable;
|
||||
|
||||
$invoice->line_items = $this->buildLineItems(rand(1, 10));
|
||||
$invoice->uses_inclusive_taxes = false;
|
||||
|
||||
// if (rand(0, 1)) {
|
||||
// $invoice->tax_name1 = 'GST';
|
||||
// $invoice->tax_rate1 = 10.00;
|
||||
// }
|
||||
|
||||
// if (rand(0, 1)) {
|
||||
// $invoice->tax_name2 = 'VAT';
|
||||
// $invoice->tax_rate2 = 17.50;
|
||||
// }
|
||||
|
||||
// if (rand(0, 1)) {
|
||||
// $invoice->tax_name3 = 'CA Sales Tax';
|
||||
// $invoice->tax_rate3 = 5;
|
||||
// }
|
||||
|
||||
// $invoice->custom_value1 = $faker->date();
|
||||
// $invoice->custom_value2 = rand(0, 1) ? 'yes' : 'no';
|
||||
|
||||
$invoice->save();
|
||||
|
||||
$invoice_calc = new InvoiceSum($invoice);
|
||||
$invoice_calc->build();
|
||||
|
||||
$invoice = $invoice_calc->getInvoice();
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$invoice->assigned_user_id = $assigned_user_id;
|
||||
}
|
||||
|
||||
$invoice->save();
|
||||
$invoice->service()->createInvitations()->markSent();
|
||||
|
||||
$this->invoice_repo->markSent($invoice);
|
||||
|
||||
if ((bool) rand(0, 2)) {
|
||||
$invoice = $invoice->service()->markPaid()->save();
|
||||
|
||||
$invoice->payments->each(function ($payment) {
|
||||
$payment->date = now()->addDays(rand(-30, 30));
|
||||
$payment->save();
|
||||
});
|
||||
}
|
||||
|
||||
event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars()));
|
||||
}
|
||||
|
||||
private function createRecurringInvoice($client, $assigned_user_id = null)
|
||||
{
|
||||
$faker = \Faker\Factory::create();
|
||||
|
||||
$invoice = RecurringInvoiceFactory::create($client->company->id, $client->user->id); //stub the company and user_id
|
||||
$invoice->client_id = $client->id;
|
||||
$invoice->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
|
||||
$invoice->last_sent_date = now()->subMonth();
|
||||
$invoice->next_send_date = now()->addMonthNoOverflow();
|
||||
|
||||
if ((bool) rand(0, 1)) {
|
||||
$dateable = Carbon::now()->subDays(rand(0, 90));
|
||||
} else {
|
||||
$dateable = Carbon::now()->addDays(rand(0, 90));
|
||||
}
|
||||
|
||||
$invoice->date = $dateable;
|
||||
|
||||
$invoice->line_items = $this->buildLineItems(rand(1, 10));
|
||||
$invoice->uses_inclusive_taxes = false;
|
||||
|
||||
// if (rand(0, 1)) {
|
||||
// $invoice->tax_name1 = 'GST';
|
||||
// $invoice->tax_rate1 = 10.00;
|
||||
// }
|
||||
|
||||
// if (rand(0, 1)) {
|
||||
// $invoice->tax_name2 = 'VAT';
|
||||
// $invoice->tax_rate2 = 17.50;
|
||||
// }
|
||||
|
||||
// if (rand(0, 1)) {
|
||||
// $invoice->tax_name3 = 'CA Sales Tax';
|
||||
// $invoice->tax_rate3 = 5;
|
||||
// }
|
||||
|
||||
// $invoice->custom_value1 = $faker->date();
|
||||
// $invoice->custom_value2 = rand(0, 1) ? 'yes' : 'no';
|
||||
|
||||
$invoice->save();
|
||||
|
||||
$invoice_calc = new InvoiceSum($invoice);
|
||||
$invoice_calc->build();
|
||||
|
||||
$invoice = $invoice_calc->getInvoice();
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$invoice->assigned_user_id = $assigned_user_id;
|
||||
}
|
||||
$invoice->number = $this->getNextRecurringInvoiceNumber($client, $invoice);
|
||||
$invoice->save();
|
||||
}
|
||||
|
||||
private function createCredit($client, $assigned_user_id = null)
|
||||
{
|
||||
$faker = \Faker\Factory::create();
|
||||
|
||||
$credit = Credit::factory()->create(['user_id' => $client->user->id, 'company_id' => $client->company->id, 'client_id' => $client->id]);
|
||||
|
||||
if ((bool) rand(0, 1)) {
|
||||
$dateable = Carbon::now()->subDays(rand(0, 90));
|
||||
} else {
|
||||
$dateable = Carbon::now()->addDays(rand(0, 90));
|
||||
}
|
||||
|
||||
$credit->date = $dateable;
|
||||
|
||||
$credit->line_items = $this->buildLineItems(rand(1, 10));
|
||||
$credit->uses_inclusive_taxes = false;
|
||||
|
||||
// if (rand(0, 1)) {
|
||||
// $credit->tax_name1 = 'GST';
|
||||
// $credit->tax_rate1 = 10.00;
|
||||
// }
|
||||
|
||||
// if (rand(0, 1)) {
|
||||
// $credit->tax_name2 = 'VAT';
|
||||
// $credit->tax_rate2 = 17.50;
|
||||
// }
|
||||
|
||||
// if (rand(0, 1)) {
|
||||
// $credit->tax_name3 = 'CA Sales Tax';
|
||||
// $credit->tax_rate3 = 5;
|
||||
// }
|
||||
|
||||
$credit->save();
|
||||
|
||||
$invoice_calc = new InvoiceSum($credit);
|
||||
$invoice_calc->build();
|
||||
|
||||
$credit = $invoice_calc->getCredit();
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$credit->assigned_user_id = $assigned_user_id;
|
||||
}
|
||||
|
||||
$credit->save();
|
||||
$credit->service()->markSent()->save();
|
||||
$credit->service()->createInvitations();
|
||||
}
|
||||
|
||||
private function createQuote($client, $assigned_user_id = null)
|
||||
{
|
||||
$faker = \Faker\Factory::create();
|
||||
|
||||
$quote = Quote::factory()->create(['user_id' => $client->user->id, 'company_id' => $client->company_id, 'client_id' => $client->id]);
|
||||
|
||||
if ((bool) rand(0, 1)) {
|
||||
$dateable = Carbon::now()->subDays(rand(1, 30));
|
||||
$dateable_due = $dateable->addDays(rand(1, 30));
|
||||
} else {
|
||||
$dateable = Carbon::now()->addDays(rand(1, 30));
|
||||
$dateable_due = $dateable->addDays(rand(-10, 30));
|
||||
}
|
||||
|
||||
$quote->date = $dateable;
|
||||
$quote->due_date = $dateable_due;
|
||||
|
||||
$quote->client_id = $client->id;
|
||||
|
||||
$quote->setRelation('client', $client);
|
||||
|
||||
$quote->line_items = $this->buildLineItems(rand(1, 10));
|
||||
$quote->uses_inclusive_taxes = false;
|
||||
|
||||
// if (rand(0, 1)) {
|
||||
// $quote->tax_name1 = 'GST';
|
||||
// $quote->tax_rate1 = 10.00;
|
||||
// }
|
||||
|
||||
// if (rand(0, 1)) {
|
||||
// $quote->tax_name2 = 'VAT';
|
||||
// $quote->tax_rate2 = 17.50;
|
||||
// }
|
||||
|
||||
// if (rand(0, 1)) {
|
||||
// $quote->tax_name3 = 'CA Sales Tax';
|
||||
// $quote->tax_rate3 = 5;
|
||||
// }
|
||||
|
||||
$quote->save();
|
||||
|
||||
$quote_calc = new InvoiceSum($quote);
|
||||
$quote_calc->build();
|
||||
|
||||
$quote = $quote_calc->getQuote();
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$quote->assigned_user_id = $assigned_user_id;
|
||||
}
|
||||
|
||||
$quote->save();
|
||||
|
||||
$quote->service()->markSent()->save();
|
||||
$quote->service()->createInvitations();
|
||||
}
|
||||
|
||||
private function buildLineItems($count = 1)
|
||||
{
|
||||
$line_items = [];
|
||||
|
||||
for ($x = 0; $x < $count; $x++) {
|
||||
$item = InvoiceItemFactory::create();
|
||||
$item->quantity = 1;
|
||||
//$item->cost = 10;
|
||||
|
||||
// if (rand(0, 1)) {
|
||||
// $item->tax_name1 = 'GST';
|
||||
// $item->tax_rate1 = 10.00;
|
||||
// }
|
||||
|
||||
// if (rand(0, 1)) {
|
||||
// $item->tax_name1 = 'VAT';
|
||||
// $item->tax_rate1 = 17.50;
|
||||
// }
|
||||
|
||||
// if (rand(0, 1)) {
|
||||
// $item->tax_name1 = 'Sales Tax';
|
||||
// $item->tax_rate1 = 5;
|
||||
// }
|
||||
|
||||
$product = Product::all()->random();
|
||||
|
||||
$item->cost = (float) $product->cost;
|
||||
$item->product_key = $product->product_key;
|
||||
$item->notes = $product->notes;
|
||||
$item->custom_value1 = $product->custom_value1;
|
||||
$item->custom_value2 = $product->custom_value2;
|
||||
$item->custom_value3 = $product->custom_value3;
|
||||
$item->custom_value4 = $product->custom_value4;
|
||||
|
||||
$line_items[] = $item;
|
||||
}
|
||||
|
||||
return $line_items;
|
||||
}
|
||||
|
||||
private function warmCache()
|
||||
{
|
||||
/* Warm up the cache !*/
|
||||
$cached_tables = config('ninja.cached_tables');
|
||||
|
||||
foreach ($cached_tables as $name => $class) {
|
||||
if (! Cache::has($name)) {
|
||||
// check that the table exists in case the migration is pending
|
||||
if (! Schema::hasTable((new $class())->getTable())) {
|
||||
continue;
|
||||
}
|
||||
if ($name == 'payment_terms') {
|
||||
$orderBy = 'num_days';
|
||||
} elseif ($name == 'fonts') {
|
||||
$orderBy = 'sort_order';
|
||||
} elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
|
||||
$orderBy = 'name';
|
||||
} else {
|
||||
$orderBy = 'id';
|
||||
}
|
||||
$tableData = $class::orderBy($orderBy)->get();
|
||||
if ($tableData->count()) {
|
||||
Cache::forever($name, $tableData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
app/Console/Commands/DesignUpdate.php
Normal file
88
app/Console/Commands/DesignUpdate.php
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Design;
|
||||
use Illuminate\Console\Command;
|
||||
use stdClass;
|
||||
|
||||
class DesignUpdate extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:design-update';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Update the system designs when changes are made.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
//always return state to first DB
|
||||
|
||||
$current_db = config('database.default');
|
||||
|
||||
if (! config('ninja.db.multi_db_enabled')) {
|
||||
$this->handleOnDb();
|
||||
} else {
|
||||
//multiDB environment, need to
|
||||
foreach (MultiDB::$dbs as $db) {
|
||||
MultiDB::setDB($db);
|
||||
|
||||
$this->handleOnDb($db);
|
||||
}
|
||||
|
||||
MultiDB::setDB($current_db);
|
||||
}
|
||||
}
|
||||
|
||||
private function handleOnDb()
|
||||
{
|
||||
foreach (Design::whereIsCustom(false)->get() as $design) {
|
||||
$invoice_design = new \App\Services\PdfMaker\Design(strtolower($design->name));
|
||||
$invoice_design->document();
|
||||
|
||||
$design_object = new stdClass;
|
||||
$design_object->includes = $invoice_design->getSectionHTML('style');
|
||||
$design_object->header = $invoice_design->getSectionHTML('header');
|
||||
$design_object->body = $invoice_design->getSectionHTML('body');
|
||||
$design_object->product = '';
|
||||
$design_object->task = '';
|
||||
$design_object->footer = $invoice_design->getSectionHTML('footer');
|
||||
|
||||
$design->design = $design_object;
|
||||
$design->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Libraries\Utils;
|
||||
use App\Models\User;
|
||||
use App\Traits\GenerateMigrationResources;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class ExportMigrations extends Command
|
||||
{
|
||||
use GenerateMigrationResources;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'migrations:export {--user=} {--random=}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Export account migrations to folder.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Note: Migrations will be stored inside of (storage/migrations) folder.');
|
||||
|
||||
if($this->option('user')) {
|
||||
$record = User::findOrFail($this->option('user'));
|
||||
return $this->export($record);
|
||||
}
|
||||
|
||||
if($this->option('random')){
|
||||
|
||||
User::all()->random(200)->each(function ($user){
|
||||
$this->export($user);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$users = User::all();
|
||||
|
||||
foreach($users as $user) {
|
||||
Auth::login($user);
|
||||
$this->export($user);
|
||||
}
|
||||
}
|
||||
|
||||
private function export($user)
|
||||
{
|
||||
$this->account = $user->account;
|
||||
|
||||
$date = date('Y-m-d');
|
||||
$accountKey = $this->account->account_key;
|
||||
|
||||
$output = fopen('php://output', 'w') or Utils::fatalError();
|
||||
|
||||
$fileName = "{$accountKey}-{$date}-invoiceninja";
|
||||
|
||||
$data['data'] = [
|
||||
'account' => $this->getAccount(),
|
||||
'company' => $this->getCompany(),
|
||||
'users' => $this->getUsers(),
|
||||
'tax_rates' => $this->getTaxRates(),
|
||||
'payment_terms' => $this->getPaymentTerms(),
|
||||
'clients' => $this->getClients(),
|
||||
'company_gateways' => $this->getCompanyGateways(),
|
||||
'client_gateway_tokens' => $this->getClientGatewayTokens(),
|
||||
'vendors' => $this->getVendors(),
|
||||
'projects' => $this->getProjects(),
|
||||
'products' => $this->getProducts(),
|
||||
'credits' => $this->getCreditsNotes(),
|
||||
'invoices' => $this->getInvoices(),
|
||||
'recurring_invoices' => $this->getRecurringInvoices(),
|
||||
'quotes' => $this->getQuotes(),
|
||||
'payments' => array_merge($this->getPayments(), $this->getCredits()),
|
||||
'documents' => $this->getDocuments(),
|
||||
'expense_categories' => $this->getExpenseCategories(),
|
||||
'task_statuses' => $this->getTaskStatuses(),
|
||||
'expenses' => $this->getExpenses(),
|
||||
'tasks' => $this->getTasks(),
|
||||
'documents' => $this->getDocuments(),
|
||||
];
|
||||
|
||||
$file = storage_path("migrations/{$fileName}.zip");
|
||||
|
||||
$zip = new \ZipArchive();
|
||||
$zip->open($file, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
|
||||
$zip->addFromString('migration.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||
$zip->close();
|
||||
|
||||
$this->info('User with id #' . $user->id . ' exported.');
|
||||
}
|
||||
}
|
||||
117
app/Console/Commands/HostedMigrations.php
Normal file
117
app/Console/Commands/HostedMigrations.php
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Exceptions\MigrationValidatorFailed;
|
||||
use App\Exceptions\NonExistingMigrationFile;
|
||||
use App\Exceptions\ProcessingMigrationArchiveFailed;
|
||||
use App\Exceptions\ResourceDependencyMissing;
|
||||
use App\Exceptions\ResourceNotAvailableForMigration;
|
||||
use App\Jobs\Util\Import;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\MigrationFailed;
|
||||
use App\Models\User;
|
||||
use App\Utils\Traits\AppSetup;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use DirectoryIterator;
|
||||
use Illuminate\Console\Command;
|
||||
use ZipArchive;
|
||||
|
||||
class HostedMigrations extends Command
|
||||
{
|
||||
use MakesHash;
|
||||
use AppSetup;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:import {--email=}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Import a v4 migration file';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->buildCache();
|
||||
|
||||
if (! MultiDB::userFindAndSetDb($this->option('email'))) {
|
||||
$this->info('Could not find a user with that email address');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$user = User::where('email', $this->option('email'))->first();
|
||||
|
||||
if (! $user) {
|
||||
$this->info('There was a problem getting the user, did you set the right DB?');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$path = public_path('storage/migrations/import');
|
||||
|
||||
$directory = new DirectoryIterator($path);
|
||||
|
||||
foreach ($directory as $file) {
|
||||
if ($file->getExtension() === 'zip') {
|
||||
$company = $user->companies()->first();
|
||||
|
||||
$this->info('Started processing: '.$file->getBasename().' at '.now());
|
||||
|
||||
$zip = new ZipArchive();
|
||||
$archive = $zip->open($file->getRealPath());
|
||||
|
||||
try {
|
||||
if (! $archive) {
|
||||
throw new ProcessingMigrationArchiveFailed('Processing migration archive failed. Migration file is possibly corrupted.');
|
||||
}
|
||||
|
||||
$filename = pathinfo($file->getRealPath(), PATHINFO_FILENAME);
|
||||
|
||||
$zip->extractTo(public_path("storage/migrations/{$filename}"));
|
||||
$zip->close();
|
||||
|
||||
$import_file = public_path("storage/migrations/$filename/migration.json");
|
||||
|
||||
Import::dispatch($import_file, $user->companies()->first(), $user);
|
||||
} catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing $e) {
|
||||
\Mail::to($this->user)->send(new MigrationFailed($e, $e->getMessage()));
|
||||
|
||||
if (app()->environment() !== 'production') {
|
||||
info($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
63
app/Console/Commands/HostedUsers.php
Normal file
63
app/Console/Commands/HostedUsers.php
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class HostedUsers extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:sync-users';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Syncs Invoice Ninja Users';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
Company::on('db-ninja-01')->each(function ($company) {
|
||||
if (Ninja::isHosted()) {
|
||||
(new \Modules\Admin\Jobs\Account\NinjaUser([], $company))->handle();
|
||||
}
|
||||
});
|
||||
|
||||
Company::on('db-ninja-02')->each(function ($company) {
|
||||
if (Ninja::isHosted()) {
|
||||
(new \Modules\Admin\Jobs\Account\NinjaUser([], $company))->handle();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
173
app/Console/Commands/ImportMigrations.php
Normal file
173
app/Console/Commands/ImportMigrations.php
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Exceptions\MigrationValidatorFailed;
|
||||
use App\Exceptions\NonExistingMigrationFile;
|
||||
use App\Exceptions\ProcessingMigrationArchiveFailed;
|
||||
use App\Exceptions\ResourceDependencyMissing;
|
||||
use App\Exceptions\ResourceNotAvailableForMigration;
|
||||
use App\Jobs\Util\Import;
|
||||
use App\Jobs\Util\StartMigration;
|
||||
use App\Mail\MigrationFailed;
|
||||
use App\Models\Account;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\User;
|
||||
use App\Utils\Traits\AppSetup;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use DirectoryIterator;
|
||||
use Faker\Factory;
|
||||
use Faker\Generator;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Str;
|
||||
use ZipArchive;
|
||||
|
||||
class ImportMigrations extends Command
|
||||
{
|
||||
use MakesHash;
|
||||
use AppSetup;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:old-import {--path=}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Massively import the migrations.';
|
||||
|
||||
/**
|
||||
* @var Generator
|
||||
*/
|
||||
private $faker;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->faker = Factory::create();
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->buildCache();
|
||||
|
||||
$path = $this->option('path') ?? public_path('storage/migrations/import');
|
||||
|
||||
$directory = new DirectoryIterator($path);
|
||||
|
||||
foreach ($directory as $file) {
|
||||
if ($file->getExtension() === 'zip') {
|
||||
$user = $this->getUser();
|
||||
$company = $this->getUser()->companies()->first();
|
||||
|
||||
$this->info('Started processing: '.$file->getBasename().' at '.now());
|
||||
|
||||
$zip = new ZipArchive();
|
||||
$archive = $zip->open($file->getRealPath());
|
||||
|
||||
try {
|
||||
if (! $archive) {
|
||||
throw new ProcessingMigrationArchiveFailed('Processing migration archive failed. Migration file is possibly corrupted.');
|
||||
}
|
||||
|
||||
$filename = pathinfo($file->getRealPath(), PATHINFO_FILENAME);
|
||||
|
||||
$zip->extractTo(public_path("storage/migrations/{$filename}"));
|
||||
$zip->close();
|
||||
|
||||
$import_file = public_path("storage/migrations/$filename/migration.json");
|
||||
|
||||
Import::dispatch($import_file, $this->getUser()->companies()->first(), $this->getUser());
|
||||
// StartMigration::dispatch($file->getRealPath(), $this->getUser(), $this->getUser()->companies()->first());
|
||||
} catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing $e) {
|
||||
\Mail::to($this->user)->send(new MigrationFailed($e, $e->getMessage()));
|
||||
|
||||
if (app()->environment() !== 'production') {
|
||||
info($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getUser(): User
|
||||
{
|
||||
$account = $this->getAccount();
|
||||
$company = $this->getCompany($account);
|
||||
|
||||
$user = User::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'email' => Str::random(10).'@example.com',
|
||||
'confirmation_code' => $this->createDbHash($company->db),
|
||||
]);
|
||||
|
||||
CompanyToken::unguard();
|
||||
|
||||
$company_token = CompanyToken::create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
'account_id' => $account->id,
|
||||
'name' => 'First token',
|
||||
'token' => Str::random(64),
|
||||
]);
|
||||
|
||||
$user->companies()->attach($company->id, [
|
||||
'account_id' => $account->id,
|
||||
'is_owner' => 1,
|
||||
'is_admin' => 1,
|
||||
'is_locked' => 0,
|
||||
'notifications' => CompanySettings::notificationDefaults(),
|
||||
'permissions' => '',
|
||||
'settings' => null,
|
||||
]);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function getAccount(): Account
|
||||
{
|
||||
return Account::factory()->create();
|
||||
}
|
||||
|
||||
public function getCompany(Account $account): Company
|
||||
{
|
||||
$company = Company::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'is_disabled' => true,
|
||||
]);
|
||||
|
||||
if (! $account->default_company_id) {
|
||||
$account->default_company_id = $company->id;
|
||||
$account->save();
|
||||
}
|
||||
|
||||
return $company;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,380 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use DB;
|
||||
use Mail;
|
||||
use Exception;
|
||||
use App\Models\DbServer;
|
||||
use App\Models\LookupCompany;
|
||||
use App\Models\LookupAccount;
|
||||
use App\Models\LookupUser;
|
||||
use App\Models\LookupContact;
|
||||
use App\Models\LookupAccountToken;
|
||||
use App\Models\LookupInvitation;
|
||||
|
||||
class InitLookup extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:init-lookup {--truncate=} {--subdomain} {--validate=} {--update=} {--company_id=} {--page_size=100} {--database=db-ninja-1}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Initialize lookup tables';
|
||||
|
||||
protected $log = '';
|
||||
protected $isValid = true;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->logMessage('Running InitLookup...');
|
||||
|
||||
config(['database.default' => DB_NINJA_LOOKUP]);
|
||||
|
||||
$database = $this->option('database');
|
||||
$dbServer = DbServer::whereName($database)->first();
|
||||
|
||||
if ($this->option('subdomain')) {
|
||||
$this->logMessage('Updating subdomains...');
|
||||
$this->popuplateSubdomains();
|
||||
} else if ($this->option('truncate')) {
|
||||
$this->logMessage('Truncating data...');
|
||||
$this->truncateTables();
|
||||
} else {
|
||||
config(['database.default' => $this->option('database')]);
|
||||
|
||||
$count = DB::table('companies')
|
||||
->where('id', '>=', $this->option('company_id') ?: 1)
|
||||
->count();
|
||||
|
||||
for ($i=0; $i<$count; $i += (int) $this->option('page_size')) {
|
||||
$this->initCompanies($dbServer->id, $i);
|
||||
}
|
||||
}
|
||||
|
||||
$this->logMessage('Results: ' . ($this->isValid ? RESULT_SUCCESS : RESULT_FAILURE));
|
||||
|
||||
if ($this->option('validate')) {
|
||||
if ($errorEmail = env('ERROR_EMAIL')) {
|
||||
Mail::raw($this->log, function ($message) use ($errorEmail, $database) {
|
||||
$message->to($errorEmail)
|
||||
->from(CONTACT_EMAIL)
|
||||
->subject("Check-Lookups [{$database}]: " . strtoupper($this->isValid ? RESULT_SUCCESS : RESULT_FAILURE));
|
||||
});
|
||||
} elseif (! $this->isValid) {
|
||||
throw new Exception('Check lookups failed!!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function popuplateSubdomains()
|
||||
{
|
||||
$data = [];
|
||||
|
||||
config(['database.default' => $this->option('database')]);
|
||||
|
||||
$accounts = DB::table('accounts')
|
||||
->orderBy('id')
|
||||
->where('subdomain', '!=', '')
|
||||
->get(['account_key', 'subdomain']);
|
||||
foreach ($accounts as $account) {
|
||||
$data[$account->account_key] = $account->subdomain;
|
||||
}
|
||||
|
||||
config(['database.default' => DB_NINJA_LOOKUP]);
|
||||
|
||||
$validate = $this->option('validate');
|
||||
$update = $this->option('update');
|
||||
|
||||
foreach ($data as $accountKey => $subdomain) {
|
||||
LookupAccount::whereAccountKey($accountKey)->update(['subdomain' => $subdomain]);
|
||||
}
|
||||
}
|
||||
|
||||
private function initCompanies($dbServerId, $offset = 0)
|
||||
{
|
||||
$data = [];
|
||||
|
||||
config(['database.default' => $this->option('database')]);
|
||||
|
||||
$companies = DB::table('companies')
|
||||
->offset($offset)
|
||||
->limit((int) $this->option('page_size'))
|
||||
->orderBy('id')
|
||||
->where('id', '>=', $this->option('company_id') ?: 1)
|
||||
->get(['id']);
|
||||
foreach ($companies as $company) {
|
||||
$data[$company->id] = $this->parseCompany($company->id);
|
||||
}
|
||||
|
||||
config(['database.default' => DB_NINJA_LOOKUP]);
|
||||
|
||||
$validate = $this->option('validate');
|
||||
$update = $this->option('update');
|
||||
|
||||
foreach ($data as $companyId => $company) {
|
||||
|
||||
$lookupCompany = false;
|
||||
if ($validate || $update) {
|
||||
$lookupCompany = LookupCompany::whereDbServerId($dbServerId)->whereCompanyId($companyId)->first();
|
||||
}
|
||||
if ($validate && ! $lookupCompany) {
|
||||
$this->logError("LookupCompany - dbServerId: {$dbServerId}, companyId: {$companyId} | Not found!");
|
||||
continue;
|
||||
}
|
||||
if (! $lookupCompany) {
|
||||
$lookupCompany = LookupCompany::create([
|
||||
'db_server_id' => $dbServerId,
|
||||
'company_id' => $companyId,
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($company as $accountKey => $account) {
|
||||
|
||||
$lookupAccount = false;
|
||||
if ($validate || $update) {
|
||||
$lookupAccount = LookupAccount::whereLookupCompanyId($lookupCompany->id)->whereAccountKey($accountKey)->first();
|
||||
}
|
||||
if ($validate && ! $lookupAccount) {
|
||||
$this->logError("LookupAccount - lookupCompanyId: {$lookupCompany->id}, accountKey {$accountKey} | Not found!");
|
||||
continue;
|
||||
}
|
||||
if (! $lookupAccount) {
|
||||
$lookupAccount = LookupAccount::create([
|
||||
'lookup_company_id' => $lookupCompany->id,
|
||||
'account_key' => $accountKey
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($account['users'] as $user) {
|
||||
$lookupUser = false;
|
||||
if ($validate || $update) {
|
||||
$lookupUser = LookupUser::whereLookupAccountId($lookupAccount->id)->whereUserId($user['user_id'])->first();
|
||||
}
|
||||
if ($validate) {
|
||||
if (! $lookupUser) {
|
||||
$this->logError("LookupUser - lookupAccountId: {$lookupAccount->id}, userId: {$user['user_id']} | Not found!");
|
||||
continue;
|
||||
} elseif ($user['email'] != $lookupUser->email || $user['oauth_user_key'] != $lookupUser->oauth_user_key || $user['referral_code'] != $lookupUser->referral_code) {
|
||||
$this->logError("LookupUser - lookupAccountId: {$lookupAccount->id}, userId: {$user['user_id']} | Out of date!");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($update && $lookupUser) {
|
||||
if ($user['email'] != $lookupUser->email || $user['oauth_user_key'] != $lookupUser->oauth_user_key || $user['referral_code'] != $lookupUser->referral_code) {
|
||||
$lookupUser->email = $user['email'];
|
||||
$lookupUser->oauth_user_key = $user['oauth_user_key'];
|
||||
$lookupUser->referral_code = $user['referral_code'];
|
||||
$lookupUser->save();
|
||||
}
|
||||
} elseif (! $lookupUser) {
|
||||
LookupUser::create([
|
||||
'lookup_account_id' => $lookupAccount->id,
|
||||
'email' => $user['email'] ?: null,
|
||||
'user_id' => $user['user_id'],
|
||||
'oauth_user_key' => $user['oauth_user_key'],
|
||||
'referral_code' => $user['referral_code'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($account['contacts'] as $contact) {
|
||||
$lookupContact = false;
|
||||
if ($validate || $update) {
|
||||
$lookupContact = LookupContact::whereLookupAccountId($lookupAccount->id)->whereContactKey($contact['contact_key'])->first();
|
||||
}
|
||||
if ($validate && ! $lookupContact) {
|
||||
$this->logError("LookupContact - lookupAccountId: {$lookupAccount->id}, contactKey: {$contact['contact_key']} | Not found!");
|
||||
continue;
|
||||
}
|
||||
if (! $lookupContact) {
|
||||
LookupContact::create([
|
||||
'lookup_account_id' => $lookupAccount->id,
|
||||
'contact_key' => $contact['contact_key'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($account['invitations'] as $invitation) {
|
||||
$lookupInvitation = false;
|
||||
if ($validate || $update) {
|
||||
$lookupInvitation = LookupInvitation::whereLookupAccountId($lookupAccount->id)->whereInvitationKey($invitation['invitation_key'])->first();
|
||||
}
|
||||
if ($validate) {
|
||||
if (! $lookupInvitation) {
|
||||
$this->logError("LookupInvitation - lookupAccountId: {$lookupAccount->id}, invitationKey: {$invitation['invitation_key']} | Not found!");
|
||||
continue;
|
||||
} elseif ($invitation['message_id'] && $lookupInvitation->message_id != $invitation['message_id']) {
|
||||
$this->logError("LookupInvitation - lookupAccountId: {$lookupAccount->id}, invitationKey: {$invitation['invitation_key']} | Not the same!");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($update && $lookupInvitation) {
|
||||
if ($invitation['message_id'] && $lookupInvitation->message_id != $invitation['message_id']) {
|
||||
$lookupInvitation->message_id = $invitation['message_id'];
|
||||
$lookupInvitation->save();
|
||||
}
|
||||
} elseif (! $lookupInvitation) {
|
||||
LookupInvitation::create([
|
||||
'lookup_account_id' => $lookupAccount->id,
|
||||
'invitation_key' => $invitation['invitation_key'],
|
||||
'message_id' => $invitation['message_id'] ?: null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($account['tokens'] as $token) {
|
||||
$lookupToken = false;
|
||||
if ($validate || $update) {
|
||||
$lookupToken = LookupAccountToken::whereLookupAccountId($lookupAccount->id)->whereToken($token['token'])->first();
|
||||
}
|
||||
if ($validate && ! $lookupToken) {
|
||||
$this->logError("LookupAccountToken - lookupAccountId: {$lookupAccount->id}, token: {$token['token']} | Not found!");
|
||||
continue;
|
||||
}
|
||||
if (! $lookupToken) {
|
||||
LookupAccountToken::create([
|
||||
'lookup_account_id' => $lookupAccount->id,
|
||||
'token' => $token['token'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function parseCompany($companyId)
|
||||
{
|
||||
$data = [];
|
||||
|
||||
config(['database.default' => $this->option('database')]);
|
||||
|
||||
$accounts = DB::table('accounts')->whereCompanyId($companyId)->orderBy('id')->get([
|
||||
'id', 'account_key'
|
||||
]);
|
||||
foreach ($accounts as $account) {
|
||||
$data[$account->account_key] = $this->parseAccount($account->id);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function parseAccount($accountId)
|
||||
{
|
||||
$data = [
|
||||
'users' => [],
|
||||
'contacts' => [],
|
||||
'invitations' => [],
|
||||
'tokens' => [],
|
||||
];
|
||||
|
||||
$users = DB::table('users')->whereAccountId($accountId)->orderBy('id')->get([
|
||||
'email',
|
||||
'id',
|
||||
'oauth_user_id',
|
||||
'oauth_provider_id',
|
||||
'referral_code',
|
||||
]);
|
||||
foreach ($users as $user) {
|
||||
$data['users'][] = [
|
||||
'email' => $user->email,
|
||||
'user_id' => $user->id,
|
||||
'oauth_user_key' => ($user->oauth_provider_id && $user->oauth_user_id) ? ($user->oauth_provider_id . '-' . $user->oauth_user_id) : null,
|
||||
'referral_code' => $user->referral_code,
|
||||
];
|
||||
}
|
||||
|
||||
$contacts = DB::table('contacts')->whereAccountId($accountId)->orderBy('id')->get([
|
||||
'contact_key'
|
||||
]);
|
||||
foreach ($contacts as $contact) {
|
||||
$data['contacts'][] = [
|
||||
'contact_key' => $contact->contact_key,
|
||||
];
|
||||
}
|
||||
|
||||
$invitations = DB::table('invitations')->whereAccountId($accountId)->orderBy('id')->get([
|
||||
'invitation_key',
|
||||
'message_id'
|
||||
]);
|
||||
foreach ($invitations as $invitation) {
|
||||
$data['invitations'][] = [
|
||||
'invitation_key' => $invitation->invitation_key,
|
||||
'message_id' => $invitation->message_id,
|
||||
];
|
||||
}
|
||||
|
||||
$tokens = DB::table('account_tokens')->whereAccountId($accountId)->orderBy('id')->get([
|
||||
'token'
|
||||
]);
|
||||
foreach ($tokens as $token) {
|
||||
$data['tokens'][] = [
|
||||
'token' => $token->token,
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function logMessage($str)
|
||||
{
|
||||
$str = date('Y-m-d h:i:s') . ' ' . $str;
|
||||
$this->info($str);
|
||||
$this->log .= $str . "\n";
|
||||
}
|
||||
|
||||
private function logError($str)
|
||||
{
|
||||
$this->isValid = false;
|
||||
$this->logMessage($str);
|
||||
}
|
||||
|
||||
private function truncateTables()
|
||||
{
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS = 0');
|
||||
DB::statement('truncate lookup_companies');
|
||||
DB::statement('truncate lookup_accounts');
|
||||
DB::statement('truncate lookup_users');
|
||||
DB::statement('truncate lookup_contacts');
|
||||
DB::statement('truncate lookup_invitations');
|
||||
DB::statement('truncate lookup_proposal_invitations');
|
||||
DB::statement('truncate lookup_account_tokens');
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
|
||||
}
|
||||
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['subdomain', null, InputOption::VALUE_OPTIONAL, 'Subdomain', null],
|
||||
['truncate', null, InputOption::VALUE_OPTIONAL, 'Truncate', null],
|
||||
['company_id', null, InputOption::VALUE_OPTIONAL, 'Company Id', null],
|
||||
['page_size', null, InputOption::VALUE_OPTIONAL, 'Page Size', null],
|
||||
['database', null, InputOption::VALUE_OPTIONAL, 'Database', null],
|
||||
['validate', null, InputOption::VALUE_OPTIONAL, 'Validate', null],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Str;
|
||||
use Nwidart\Modules\Commands\GeneratorCommand;
|
||||
use Nwidart\Modules\Support\Stub;
|
||||
|
||||
use Nwidart\Modules\Traits\ModuleCommandTrait;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
class MakeClass extends GeneratorCommand
|
||||
{
|
||||
use ModuleCommandTrait;
|
||||
|
||||
protected $argumentName = 'name';
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'ninja:make-class';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create class stub';
|
||||
|
||||
protected function getArguments()
|
||||
{
|
||||
return [
|
||||
['name', InputArgument::REQUIRED, 'The name of the module.'],
|
||||
['module', InputArgument::REQUIRED, 'The name of module will be used.'],
|
||||
['class', InputArgument::REQUIRED, 'The name of the class.'],
|
||||
['prefix', InputArgument::OPTIONAL, 'The prefix of the class.'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['fields', null, InputOption::VALUE_OPTIONAL, 'The model attributes.', null],
|
||||
['filename', null, InputOption::VALUE_OPTIONAL, 'The class filename.', null],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTemplateContents()
|
||||
{
|
||||
$module = $this->laravel['modules']->findOrFail($this->getModuleName());
|
||||
$path = str_replace('/', '\\', config('modules.paths.generator.' . $this->argument('class')));
|
||||
|
||||
return (new Stub('/' . $this->argument('prefix') . $this->argument('class') . '.stub', [
|
||||
'NAMESPACE' => $this->getClassNamespace($module) . '\\' . $path,
|
||||
'LOWER_NAME' => $module->getLowerName(),
|
||||
'CLASS' => $this->getClass(),
|
||||
'STUDLY_NAME' => Str::studly($module->getLowerName()),
|
||||
'DATATABLE_COLUMNS' => $this->getColumns(),
|
||||
'FORM_FIELDS' => $this->getFormFields(),
|
||||
'DATABASE_FIELDS' => $this->getDatabaseFields($module),
|
||||
'TRANSFORMER_FIELDS' => $this->getTransformerFields($module),
|
||||
]))->render();
|
||||
}
|
||||
|
||||
public function getDestinationFilePath()
|
||||
{
|
||||
$path = $this->laravel['modules']->getModulePath($this->getModuleName());
|
||||
$seederPath = $this->laravel['modules']->config('paths.generator.' . $this->argument('class'));
|
||||
|
||||
return $path . $seederPath . '/' . $this->getFileName() . '.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getFileName()
|
||||
{
|
||||
if ($this->option('filename')) {
|
||||
return $this->option('filename');
|
||||
}
|
||||
|
||||
return studly_case($this->argument('prefix')) . studly_case($this->argument('name')) . Str::studly($this->argument('class'));
|
||||
}
|
||||
|
||||
protected function getColumns()
|
||||
{
|
||||
$fields = $this->option('fields');
|
||||
$fields = explode(',', $fields);
|
||||
$str = '';
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if (! $field) {
|
||||
continue;
|
||||
}
|
||||
$field = explode(':', $field)[0];
|
||||
$str .= '[
|
||||
\''. $field . '\',
|
||||
function ($model) {
|
||||
return $model->' . $field . ';
|
||||
}
|
||||
],';
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
protected function getFormFields()
|
||||
{
|
||||
$fields = $this->option('fields');
|
||||
$fields = explode(',', $fields);
|
||||
$str = '';
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if (! $field) {
|
||||
continue;
|
||||
}
|
||||
$parts = explode(':', $field);
|
||||
$field = $parts[0];
|
||||
$type = $parts[1];
|
||||
|
||||
if ($type == 'text') {
|
||||
$str .= "{!! Former::textarea('" . $field . "') !!}\n";
|
||||
} else {
|
||||
$str .= "{!! Former::text('" . $field . "') !!}\n";
|
||||
}
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
protected function getDatabaseFields($module)
|
||||
{
|
||||
$fields = $this->option('fields');
|
||||
$fields = explode(',', $fields);
|
||||
$str = '';
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if (! $field) {
|
||||
continue;
|
||||
}
|
||||
$field = explode(':', $field)[0];
|
||||
$str .= "'" . $module->getLowerName() . ".{$field}', ";
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
protected function getTransformerFields($module)
|
||||
{
|
||||
$fields = $this->option('fields');
|
||||
$fields = explode(',', $fields);
|
||||
$str = '';
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if (! $field) {
|
||||
continue;
|
||||
}
|
||||
$field = explode(':', $field)[0];
|
||||
$str .= "'{$field}' => $" . $module->getLowerName() . "->$field,\n ";
|
||||
}
|
||||
|
||||
return rtrim($str);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Artisan;
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
|
||||
class MakeModule extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:make-module {name : Module name} {fields? : Model fields} {--migrate : Run module migrations} {--p|--plain : Generate only base module scaffold}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generate Module CRUD';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$name = $this->argument('name');
|
||||
$fields = $this->argument('fields');
|
||||
$migrate = $this->option('migrate');
|
||||
$plain = $this->option('plain');
|
||||
$lower = strtolower($name);
|
||||
|
||||
// convert 'name:string,description:text' to 'name,description'
|
||||
$fillable = explode(',', $fields);
|
||||
$fillable = array_map(function ($item) {
|
||||
return explode(':', $item)[0];
|
||||
}, $fillable);
|
||||
$fillable = implode(',', $fillable);
|
||||
|
||||
ProgressBar::setFormatDefinition('custom', '%current%/%max% %elapsed:6s% [%bar%] %percent:3s%% %message%');
|
||||
$progressBar = $this->output->createProgressBar($plain ? 2 : ($migrate ? 15 : 14));
|
||||
$progressBar->setFormat('custom');
|
||||
|
||||
$this->info("Creating module: {$name}...");
|
||||
$progressBar->setMessage("Starting module creation...");
|
||||
Artisan::call('module:make', ['name' => [$name]]);
|
||||
$progressBar->advance();
|
||||
|
||||
if (! $plain) {
|
||||
$progressBar->setMessage("Creating migrations...");
|
||||
Artisan::call('module:make-migration', ['name' => "create_{$lower}_table", '--fields' => $fields, 'module' => $name]);
|
||||
$progressBar->advance();
|
||||
|
||||
$progressBar->setMessage("Creating models...");
|
||||
Artisan::call('module:make-model', ['model' => $name, 'module' => $name, '--fillable' => $fillable]);
|
||||
$progressBar->advance();
|
||||
|
||||
$progressBar->setMessage("Creating views...");
|
||||
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'views', '--fields' => $fields, '--filename' => 'edit.blade']);
|
||||
$progressBar->advance();
|
||||
|
||||
$progressBar->setMessage("Creating datatables...");
|
||||
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'datatable', '--fields' => $fields]);
|
||||
$progressBar->advance();
|
||||
|
||||
$progressBar->setMessage("Creating repositories...");
|
||||
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'repository', '--fields' => $fields]);
|
||||
$progressBar->advance();
|
||||
|
||||
$progressBar->setMessage("Creating presenters...");
|
||||
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'presenter']);
|
||||
$progressBar->advance();
|
||||
|
||||
$progressBar->setMessage("Creating requests...");
|
||||
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'request']);
|
||||
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'request', 'prefix' => 'create']);
|
||||
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'request', 'prefix' => 'update']);
|
||||
$progressBar->advance();
|
||||
|
||||
$progressBar->setMessage("Creating api-controllers...");
|
||||
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'api-controller']);
|
||||
$progressBar->advance();
|
||||
|
||||
$progressBar->setMessage("Creating transformers...");
|
||||
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'transformer', '--fields' => $fields]);
|
||||
$progressBar->advance();
|
||||
|
||||
// if the migrate flag was specified, run the migrations
|
||||
if ($migrate) {
|
||||
$progressBar->setMessage("Running migrations...");
|
||||
Artisan::call('module:migrate', ['module' => $name]);
|
||||
$progressBar->advance();
|
||||
}
|
||||
}
|
||||
|
||||
$progressBar->setMessage("Creating policies...");
|
||||
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'policy']);
|
||||
$progressBar->advance();
|
||||
|
||||
$progressBar->setMessage("Creating auth-providers...");
|
||||
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'auth-provider']);
|
||||
$progressBar->advance();
|
||||
|
||||
$progressBar->setMessage("Creating translations...");
|
||||
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'lang', '--filename' => 'texts']);
|
||||
$progressBar->advance();
|
||||
|
||||
$progressBar->setMessage("Dumping module auto-load...");
|
||||
Artisan::call('module:dump');
|
||||
$progressBar->finish();
|
||||
$progressBar->clear();
|
||||
|
||||
$this->info('Done');
|
||||
|
||||
if (!$migrate && !$plain) {
|
||||
$this->info("==> Migrations were not run because the --migrate flag was not specified.");
|
||||
$this->info("==> Use the following command to run the migrations:\nphp artisan module:migrate $name");
|
||||
}
|
||||
}
|
||||
|
||||
protected function getArguments()
|
||||
{
|
||||
return [
|
||||
['name', InputArgument::REQUIRED, 'The name of the module.'],
|
||||
['fields', InputArgument::OPTIONAL, 'The fields of the module.'],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['migrate', null, InputOption::VALUE_NONE, 'Run module migrations.', null],
|
||||
['plain', 'p', InputOption::VALUE_NONE, 'Generate only base module scaffold.', null],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,18 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Utils\CurlUtils;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\DbServer;
|
||||
use App\Models\User;
|
||||
use App\Models\Company;
|
||||
use App\Libraries\CurlUtils;
|
||||
|
||||
class MobileLocalization extends Command
|
||||
{
|
||||
|
|
@ -24,7 +30,6 @@ class MobileLocalization extends Command
|
|||
*/
|
||||
protected $description = 'Generate mobile localization resources';
|
||||
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
|
|
@ -84,9 +89,9 @@ class MobileLocalization extends Command
|
|||
$text = $resources->$key;
|
||||
}
|
||||
|
||||
$text = str_replace(array('<b>', '</b>'), '', $text);
|
||||
$text = str_replace(array('<i>', '</i>'), '', $text);
|
||||
$text = str_replace(array('<strong>', '</strong>'), '', $text);
|
||||
$text = str_replace(['<b>', '</b>'], '', $text);
|
||||
$text = str_replace(['<i>', '</i>'], '', $text);
|
||||
$text = str_replace(['<strong>', '</strong>'], '', $text);
|
||||
|
||||
echo "'$key': '$text',\n";
|
||||
}
|
||||
|
|
@ -104,11 +109,12 @@ class MobileLocalization extends Command
|
|||
$end = strpos($data, '},', $start);
|
||||
$data = substr($data, $start, $end - $start - 5);
|
||||
|
||||
$data = str_replace("\n", "", $data);
|
||||
$data = str_replace("\"", "\'", $data);
|
||||
$data = str_replace("'", "\"", $data);
|
||||
$data = str_replace("\n", '', $data);
|
||||
$data = str_replace("\'", "\#", $data);
|
||||
$data = str_replace("'", '"', $data);
|
||||
$data = str_replace("\#", "'", $data);
|
||||
|
||||
return json_decode('{' . rtrim($data, ',') . '}');
|
||||
return json_decode('{'.rtrim($data, ',').'}');
|
||||
}
|
||||
|
||||
protected function getOptions()
|
||||
|
|
@ -117,5 +123,4 @@ class MobileLocalization extends Command
|
|||
['type', null, InputOption::VALUE_OPTIONAL, 'Type', null],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
120
app/Console/Commands/OpenApiYaml.php
Normal file
120
app/Console/Commands/OpenApiYaml.php
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use DirectoryIterator;
|
||||
use Faker\Factory;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class OpenApiYaml extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:openapi';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Build OpenApi YAML';
|
||||
|
||||
private array $directories = [
|
||||
'/components/schemas',
|
||||
'/paths/'
|
||||
];
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->faker = Factory::create();
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$path = base_path('openapi');
|
||||
|
||||
$directory = new DirectoryIterator($path);
|
||||
|
||||
$this->info($directory);
|
||||
|
||||
foreach ($directory as $file) {
|
||||
$this->info($file);
|
||||
}
|
||||
|
||||
Storage::disk('base')->delete('/openapi/api-docs.yaml');
|
||||
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/info.yaml'));
|
||||
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/paths.yaml'));
|
||||
|
||||
//iterate paths
|
||||
$directory = new DirectoryIterator($path . '/paths/');
|
||||
|
||||
foreach ($directory as $file) {
|
||||
if ($file->isFile() && ! $file->isDot()) {
|
||||
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents("{$path}/paths/{$file->getFilename()}"));
|
||||
}
|
||||
}
|
||||
|
||||
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/components.yaml'));
|
||||
|
||||
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/components/responses.yaml'));
|
||||
|
||||
$directory = new DirectoryIterator($path . '/components/responses/');
|
||||
|
||||
foreach ($directory as $file) {
|
||||
if ($file->isFile() && ! $file->isDot()) {
|
||||
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents("{$path}/components/responses/{$file->getFilename()}"));
|
||||
}
|
||||
}
|
||||
|
||||
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/components/parameters.yaml'));
|
||||
|
||||
$directory = new DirectoryIterator($path . '/components/parameters/');
|
||||
|
||||
foreach ($directory as $file) {
|
||||
if ($file->isFile() && ! $file->isDot()) {
|
||||
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents("{$path}/components/parameters/{$file->getFilename()}"));
|
||||
}
|
||||
}
|
||||
|
||||
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/components/schemas.yaml'));
|
||||
|
||||
//iterate schemas
|
||||
|
||||
$directory = new DirectoryIterator($path . '/components/schemas/');
|
||||
|
||||
foreach ($directory as $file) {
|
||||
if ($file->isFile() && ! $file->isDot()) {
|
||||
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents("{$path}/components/schemas/{$file->getFilename()}"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/components/schemas/account.yaml'));
|
||||
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/misc/misc.yaml'));
|
||||
}
|
||||
}
|
||||
94
app/Console/Commands/PostUpdate.php
Normal file
94
app/Console/Commands/PostUpdate.php
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Jobs\Util\VersionCheck;
|
||||
use App\Utils\Traits\AppSetup;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
|
||||
class PostUpdate extends Command
|
||||
{
|
||||
use AppSetup;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:post-update';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Run basic upgrade commands';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
set_time_limit(0);
|
||||
|
||||
info('running post update');
|
||||
|
||||
try {
|
||||
Artisan::call('migrate', ['--force' => true]);
|
||||
} catch (\Exception $e) {
|
||||
info("I wasn't able to migrate the data.");
|
||||
}
|
||||
|
||||
info('finished migrating');
|
||||
|
||||
$output = [];
|
||||
|
||||
// exec('vendor/bin/composer install --no-dev -o', $output);
|
||||
|
||||
info(print_r($output, 1));
|
||||
info('finished running composer install ');
|
||||
|
||||
try {
|
||||
Artisan::call('optimize');
|
||||
} catch (\Exception $e) {
|
||||
info("I wasn't able to optimize.");
|
||||
}
|
||||
|
||||
info('optimized');
|
||||
|
||||
try {
|
||||
Artisan::call('view:clear');
|
||||
} catch (\Exception $e) {
|
||||
info("I wasn't able to clear the views.");
|
||||
}
|
||||
|
||||
info('view cleared');
|
||||
|
||||
try {
|
||||
Artisan::call('queue:restart');
|
||||
} catch (\Exception $e) {
|
||||
info("I wasn't able to restart the queue.");
|
||||
}
|
||||
|
||||
info('queue restarted');
|
||||
|
||||
$this->buildCache(true);
|
||||
|
||||
VersionCheck::dispatch();
|
||||
|
||||
info('Sent for version check');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use DB;
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Class PruneData.
|
||||
*/
|
||||
class PruneData extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'ninja:prune-data';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Delete inactive accounts';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->info(date('r').' Running PruneData...');
|
||||
|
||||
if ($database = $this->option('database')) {
|
||||
config(['database.default' => $database]);
|
||||
}
|
||||
|
||||
// delete accounts who never registered, didn't create any invoices,
|
||||
// hansn't logged in within the past 6 months and isn't linked to another account
|
||||
$sql = 'select c.id
|
||||
from companies c
|
||||
left join accounts a on a.company_id = c.id
|
||||
left join clients cl on cl.account_id = a.id
|
||||
left join tasks t on t.account_id = a.id
|
||||
left join expenses e on e.account_id = a.id
|
||||
left join users u on u.account_id = a.id and u.registered = 1
|
||||
where c.created_at < DATE_SUB(now(), INTERVAL 6 MONTH)
|
||||
and c.trial_started is null
|
||||
and c.plan is null
|
||||
group by c.id
|
||||
having count(cl.id) = 0
|
||||
and count(t.id) = 0
|
||||
and count(e.id) = 0
|
||||
and count(u.id) = 0';
|
||||
|
||||
$results = DB::select($sql);
|
||||
|
||||
foreach ($results as $result) {
|
||||
$this->info("Deleting company: {$result->id}");
|
||||
try {
|
||||
DB::table('companies')
|
||||
->where('id', '=', $result->id)
|
||||
->delete();
|
||||
} catch (\Illuminate\Database\QueryException $e) {
|
||||
// most likely because a user_account record exists which doesn't cascade delete
|
||||
$this->info("Unable to delete companyId: {$result->id}");
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('Done');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getArguments()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['database', null, InputOption::VALUE_OPTIONAL, 'Database', null],
|
||||
];
|
||||
}
|
||||
}
|
||||
69
app/Console/Commands/ReactBuilder.php
Normal file
69
app/Console/Commands/ReactBuilder.php
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class ReactBuilder extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:react';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Builds blade component for react includes';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$includes = '';
|
||||
|
||||
$directoryIterator = new \RecursiveDirectoryIterator(public_path('react'), \RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
|
||||
foreach (new \RecursiveIteratorIterator($directoryIterator) as $file) {
|
||||
if (str_contains($file->getFileName(), '.js') && !strpos($file->getFileName(), '.json')) {
|
||||
if (str_contains($file->getFileName(), 'index.')) {
|
||||
$includes .= '<script type="module" crossorigin src="/react/'.$file->getFileName().'"></script>'."\n";
|
||||
} else {
|
||||
$includes .= '<link rel="modulepreload" href="/react/'.$file->getFileName().'">'."\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (str_contains($file->getFileName(), '.css')) {
|
||||
$includes .= '<link rel="stylesheet" href="/react/'.$file->getFileName().'">'."\n";
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents(resource_path('views/react/head.blade.php'), $includes);
|
||||
}
|
||||
}
|
||||
52
app/Console/Commands/RecurringCommand.php
Normal file
52
app/Console/Commands/RecurringCommand.php
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Jobs\Cron\RecurringInvoicesCron;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class RecurringCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:send-recurring';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Sends the recurring invoices';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
(new RecurringInvoicesCron())->handle();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Document;
|
||||
use DateTime;
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Class RemoveOrphanedDocuments.
|
||||
*/
|
||||
class RemoveOrphanedDocuments extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'ninja:remove-orphaned-documents';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Removes old documents not associated with an expense or invoice';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->info(date('r').' Running RemoveOrphanedDocuments...');
|
||||
|
||||
if ($database = $this->option('database')) {
|
||||
config(['database.default' => $database]);
|
||||
}
|
||||
|
||||
$documents = Document::whereRaw('invoice_id IS NULL AND expense_id IS NULL AND updated_at <= ?', [new DateTime('-1 hour')])
|
||||
->get();
|
||||
|
||||
$this->info($documents->count() . ' orphaned document(s) found');
|
||||
|
||||
foreach ($documents as $document) {
|
||||
$document->delete();
|
||||
}
|
||||
|
||||
$this->info('Done');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getArguments()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['database', null, InputOption::VALUE_OPTIONAL, 'Database', null],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Utils;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Class ResetData.
|
||||
*/
|
||||
class ResetData extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'ninja:reset-data';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Reset data';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->info(date('r') . ' Running ResetData...');
|
||||
|
||||
if (! Utils::isNinjaDev()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($database = $this->option('database')) {
|
||||
config(['database.default' => $database]);
|
||||
}
|
||||
|
||||
Artisan::call('migrate:reset');
|
||||
Artisan::call('migrate');
|
||||
Artisan::call('db:seed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['fix', null, InputOption::VALUE_OPTIONAL, 'Fix data', null],
|
||||
['client_id', null, InputOption::VALUE_OPTIONAL, 'Client id', null],
|
||||
['database', null, InputOption::VALUE_OPTIONAL, 'Database', null],
|
||||
];
|
||||
}
|
||||
}
|
||||
87
app/Console/Commands/S3Cleanup.php
Normal file
87
app/Console/Commands/S3Cleanup.php
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class S3Cleanup extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:s3-cleanup';
|
||||
|
||||
protected $log = '';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Remove orphan folders/files';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (!Ninja::isHosted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$c1 = Company::on('db-ninja-01')->pluck('company_key');
|
||||
$c2 = Company::on('db-ninja-02')->pluck('company_key');
|
||||
|
||||
$merged = $c1->merge($c2)->toArray();
|
||||
|
||||
$directories = Storage::disk(config('filesystems.default'))->directories();
|
||||
|
||||
$this->LogMessage('Disk Cleanup');
|
||||
|
||||
foreach ($directories as $dir) {
|
||||
if (! in_array($dir, $merged)) {
|
||||
$this->logMessage("Deleting $dir");
|
||||
|
||||
/* Ensure we are not deleting the root folder */
|
||||
if (strlen($dir) > 1) {
|
||||
Storage::disk(config('filesystems.default'))->deleteDirectory($dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->logMessage('exiting');
|
||||
}
|
||||
|
||||
private function logMessage($str)
|
||||
{
|
||||
$str = date('Y-m-d h:i:s').' '.$str;
|
||||
$this->info($str);
|
||||
$this->log .= $str."\n";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Account;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\RecurringExpense;
|
||||
use App\Ninja\Repositories\InvoiceRepository;
|
||||
use App\Ninja\Repositories\RecurringExpenseRepository;
|
||||
use App\Jobs\SendInvoiceEmail;
|
||||
use DateTime;
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Auth;
|
||||
use Exception;
|
||||
use Utils;
|
||||
|
||||
/**
|
||||
* Class SendRecurringInvoices.
|
||||
*/
|
||||
class SendRecurringInvoices extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'ninja:send-invoices';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Send recurring invoices';
|
||||
|
||||
/**
|
||||
* @var InvoiceRepository
|
||||
*/
|
||||
protected $invoiceRepo;
|
||||
|
||||
/**
|
||||
* SendRecurringInvoices constructor.
|
||||
*
|
||||
* @param InvoiceRepository $invoiceRepo
|
||||
*/
|
||||
public function __construct(InvoiceRepository $invoiceRepo, RecurringExpenseRepository $recurringExpenseRepo)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->invoiceRepo = $invoiceRepo;
|
||||
$this->recurringExpenseRepo = $recurringExpenseRepo;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->info(date('r') . ' Running SendRecurringInvoices...');
|
||||
|
||||
if ($database = $this->option('database')) {
|
||||
config(['database.default' => $database]);
|
||||
}
|
||||
|
||||
$this->resetCounters();
|
||||
$this->createInvoices();
|
||||
$this->createExpenses();
|
||||
|
||||
$this->info(date('r') . ' Done');
|
||||
}
|
||||
|
||||
private function resetCounters()
|
||||
{
|
||||
$accounts = Account::where('reset_counter_frequency_id', '>', 0)
|
||||
->orderBy('id', 'asc')
|
||||
->get();
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$account->checkCounterReset();
|
||||
}
|
||||
}
|
||||
|
||||
private function createInvoices()
|
||||
{
|
||||
$today = new DateTime();
|
||||
|
||||
$invoices = Invoice::with('account.timezone', 'invoice_items', 'client', 'user')
|
||||
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND is_recurring IS TRUE AND is_public IS TRUE AND frequency_id > 0 AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)', [$today, $today])
|
||||
->orderBy('id', 'asc')
|
||||
->cursor();
|
||||
$this->info(date('r ') . ' Recurring invoice(s) found');
|
||||
|
||||
foreach ($invoices as $recurInvoice) {
|
||||
$shouldSendToday = $recurInvoice->shouldSendToday();
|
||||
|
||||
if (! $shouldSendToday) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->info(date('r') . ' Processing Invoice: '. $recurInvoice->id);
|
||||
|
||||
$account = $recurInvoice->account;
|
||||
$account->loadLocalizationSettings($recurInvoice->client);
|
||||
Auth::loginUsingId($recurInvoice->activeUser()->id);
|
||||
|
||||
try {
|
||||
$invoice = $this->invoiceRepo->createRecurringInvoice($recurInvoice);
|
||||
if ($invoice && ! $invoice->isPaid() && $account->auto_email_invoice) {
|
||||
$this->info(date('r') . ' Not billed - Sending Invoice');
|
||||
dispatch(new SendInvoiceEmail($invoice, $invoice->user_id));
|
||||
} elseif ($invoice) {
|
||||
$this->info(date('r') . ' Successfully billed invoice');
|
||||
}
|
||||
} catch (Exception $exception) {
|
||||
$this->info(date('r') . ' Error: ' . $exception->getMessage());
|
||||
Utils::logError($exception);
|
||||
}
|
||||
|
||||
Auth::logout();
|
||||
}
|
||||
}
|
||||
|
||||
private function createExpenses()
|
||||
{
|
||||
$today = new DateTime();
|
||||
|
||||
$expenses = RecurringExpense::with('client')
|
||||
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)', [$today, $today])
|
||||
->orderBy('id', 'asc')
|
||||
->get();
|
||||
$this->info(date('r ') . $expenses->count() . ' recurring expenses(s) found');
|
||||
|
||||
foreach ($expenses as $expense) {
|
||||
$shouldSendToday = $expense->shouldSendToday();
|
||||
|
||||
if (! $shouldSendToday) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->info(date('r') . ' Processing Expense: '. $expense->id);
|
||||
$this->recurringExpenseRepo->createRecurringExpense($expense);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getArguments()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['database', null, InputOption::VALUE_OPTIONAL, 'Database', null],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,280 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Libraries\CurlUtils;
|
||||
use Carbon;
|
||||
use Str;
|
||||
use Cache;
|
||||
use Utils;
|
||||
use Exception;
|
||||
use DateTime;
|
||||
use Auth;
|
||||
use App\Jobs\SendInvoiceEmail;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Currency;
|
||||
use App\Ninja\Mailers\UserMailer;
|
||||
use App\Ninja\Repositories\AccountRepository;
|
||||
use App\Ninja\Repositories\InvoiceRepository;
|
||||
use App\Models\ScheduledReport;
|
||||
use App\Services\PaymentService;
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use App\Jobs\ExportReportResults;
|
||||
use App\Jobs\RunReport;
|
||||
|
||||
/**
|
||||
* Class SendReminders.
|
||||
*/
|
||||
class SendReminders extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'ninja:send-reminders';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Send reminder emails';
|
||||
|
||||
/**
|
||||
* @var InvoiceRepository
|
||||
*/
|
||||
protected $invoiceRepo;
|
||||
|
||||
/**
|
||||
* @var accountRepository
|
||||
*/
|
||||
protected $accountRepo;
|
||||
|
||||
/**
|
||||
* @var PaymentService
|
||||
*/
|
||||
protected $paymentService;
|
||||
|
||||
/**
|
||||
* SendReminders constructor.
|
||||
*
|
||||
* @param Mailer $mailer
|
||||
* @param InvoiceRepository $invoiceRepo
|
||||
* @param accountRepository $accountRepo
|
||||
*/
|
||||
public function __construct(InvoiceRepository $invoiceRepo, PaymentService $paymentService, AccountRepository $accountRepo, UserMailer $userMailer)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->paymentService = $paymentService;
|
||||
$this->invoiceRepo = $invoiceRepo;
|
||||
$this->accountRepo = $accountRepo;
|
||||
$this->userMailer = $userMailer;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->info(date('r') . ' Running SendReminders...');
|
||||
|
||||
if ($database = $this->option('database')) {
|
||||
config(['database.default' => $database]);
|
||||
}
|
||||
|
||||
$this->billInvoices();
|
||||
$this->chargeLateFees();
|
||||
$this->sendReminderEmails();
|
||||
$this->sendScheduledReports();
|
||||
$this->loadExchangeRates();
|
||||
|
||||
$this->info(date('r') . ' Done');
|
||||
|
||||
if ($errorEmail = env('ERROR_EMAIL')) {
|
||||
\Mail::raw('EOM', function ($message) use ($errorEmail, $database) {
|
||||
$message->to($errorEmail)
|
||||
->from(CONTACT_EMAIL)
|
||||
->subject("SendReminders [{$database}]: Finished successfully");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private function billInvoices()
|
||||
{
|
||||
$today = new DateTime();
|
||||
|
||||
$delayedAutoBillInvoices = Invoice::with('account.timezone', 'recurring_invoice', 'invoice_items', 'client', 'user')
|
||||
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND is_recurring IS FALSE AND is_public IS TRUE
|
||||
AND balance > 0 AND due_date = ? AND recurring_invoice_id IS NOT NULL',
|
||||
[$today->format('Y-m-d')])
|
||||
->orderBy('invoices.id', 'asc')
|
||||
->get();
|
||||
$this->info(date('r ') . $delayedAutoBillInvoices->count() . ' due recurring invoice instance(s) found');
|
||||
|
||||
/** @var Invoice $invoice */
|
||||
foreach ($delayedAutoBillInvoices as $invoice) {
|
||||
if ($invoice->isPaid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($invoice->getAutoBillEnabled() && $invoice->client->autoBillLater()) {
|
||||
$this->info(date('r') . ' Processing Autobill-delayed Invoice: ' . $invoice->id);
|
||||
Auth::loginUsingId($invoice->activeUser()->id);
|
||||
$this->paymentService->autoBillInvoice($invoice);
|
||||
Auth::logout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function chargeLateFees()
|
||||
{
|
||||
$accounts = $this->accountRepo->findWithFees();
|
||||
$this->info(date('r ') . $accounts->count() . ' accounts found with fees enabled');
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
if (! $account->hasFeature(FEATURE_EMAIL_TEMPLATES_REMINDERS)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$invoices = $this->invoiceRepo->findNeedingReminding($account, false);
|
||||
$this->info(date('r ') . $account->name . ': ' . $invoices->count() . ' invoices found');
|
||||
|
||||
foreach ($invoices as $invoice) {
|
||||
if ($reminder = $account->getInvoiceReminder($invoice, false)) {
|
||||
$this->info(date('r') . ' Charge fee: ' . $invoice->id);
|
||||
$account->loadLocalizationSettings($invoice->client); // support trans to add fee line item
|
||||
$number = preg_replace('/[^0-9]/', '', $reminder);
|
||||
|
||||
$amount = $account->account_email_settings->{"late_fee{$number}_amount"};
|
||||
$percent = $account->account_email_settings->{"late_fee{$number}_percent"};
|
||||
$this->invoiceRepo->setLateFee($invoice, $amount, $percent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function sendReminderEmails()
|
||||
{
|
||||
$accounts = $this->accountRepo->findWithReminders();
|
||||
$this->info(date('r ') . count($accounts) . ' accounts found with reminders enabled');
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
if (! $account->hasFeature(FEATURE_EMAIL_TEMPLATES_REMINDERS)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// standard reminders
|
||||
$invoices = $this->invoiceRepo->findNeedingReminding($account);
|
||||
$this->info(date('r ') . $account->name . ': ' . $invoices->count() . ' invoices found');
|
||||
|
||||
foreach ($invoices as $invoice) {
|
||||
if ($reminder = $account->getInvoiceReminder($invoice)) {
|
||||
if ($invoice->last_sent_date == date('Y-m-d')) {
|
||||
continue;
|
||||
}
|
||||
$this->info(date('r') . ' Send email: ' . $invoice->id);
|
||||
dispatch(new SendInvoiceEmail($invoice, $invoice->user_id, $reminder));
|
||||
}
|
||||
}
|
||||
|
||||
// endless reminders
|
||||
$invoices = $this->invoiceRepo->findNeedingEndlessReminding($account);
|
||||
$this->info(date('r ') . $account->name . ': ' . $invoices->count() . ' endless invoices found');
|
||||
|
||||
foreach ($invoices as $invoice) {
|
||||
if ($invoice->last_sent_date == date('Y-m-d')) {
|
||||
continue;
|
||||
}
|
||||
$this->info(date('r') . ' Send email: ' . $invoice->id);
|
||||
dispatch(new SendInvoiceEmail($invoice, $invoice->user_id, 'reminder4'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function sendScheduledReports()
|
||||
{
|
||||
$scheduledReports = ScheduledReport::where('send_date', '<=', date('Y-m-d'))
|
||||
->with('user', 'account.company')
|
||||
->get();
|
||||
$this->info(date('r ') . $scheduledReports->count() . ' scheduled reports');
|
||||
|
||||
foreach ($scheduledReports as $scheduledReport) {
|
||||
$this->info(date('r') . ' Processing report: ' . $scheduledReport->id);
|
||||
|
||||
$user = $scheduledReport->user;
|
||||
$account = $scheduledReport->account;
|
||||
$account->loadLocalizationSettings();
|
||||
|
||||
if (! $account->hasFeature(FEATURE_REPORTS)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$config = (array) json_decode($scheduledReport->config);
|
||||
$reportType = $config['report_type'];
|
||||
|
||||
// send email as user
|
||||
auth()->onceUsingId($user->id);
|
||||
|
||||
$report = dispatch_now(new RunReport($scheduledReport->user, $reportType, $config, true));
|
||||
$file = dispatch_now(new ExportReportResults($scheduledReport->user, $config['export_format'], $reportType, $report->exportParams));
|
||||
|
||||
if ($file) {
|
||||
try {
|
||||
$this->userMailer->sendScheduledReport($scheduledReport, $file);
|
||||
$this->info(date('r') . ' Sent report');
|
||||
} catch (Exception $exception) {
|
||||
$this->info(date('r') . ' ERROR: ' . $exception->getMessage());
|
||||
}
|
||||
} else {
|
||||
$this->info(date('r') . ' ERROR: Failed to run report');
|
||||
}
|
||||
|
||||
$scheduledReport->updateSendDate();
|
||||
|
||||
auth()->logout();
|
||||
}
|
||||
}
|
||||
|
||||
private function loadExchangeRates()
|
||||
{
|
||||
if (Utils::isNinjaDev()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (config('ninja.exchange_rates_enabled')) {
|
||||
$this->info(date('r') . ' Loading latest exchange rates...');
|
||||
|
||||
$response = CurlUtils::get(config('ninja.exchange_rates_url'));
|
||||
$data = json_decode($response);
|
||||
|
||||
if ($data && property_exists($data, 'rates')) {
|
||||
Currency::whereCode(config('ninja.exchange_rates_base'))->update(['exchange_rate' => 1]);
|
||||
|
||||
foreach ($data->rates as $code => $rate) {
|
||||
Currency::whereCode($code)->update(['exchange_rate' => $rate]);
|
||||
}
|
||||
} else {
|
||||
$this->info(date('r') . ' Error: failed to load exchange rates - ' . $response);
|
||||
\DB::table('currencies')->update(['exchange_rate' => 1]);
|
||||
}
|
||||
} else {
|
||||
\DB::table('currencies')->update(['exchange_rate' => 1]);
|
||||
}
|
||||
|
||||
CurlUtils::get(SITE_URL . '?clear_cache=true');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getArguments()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['database', null, InputOption::VALUE_OPTIONAL, 'Database', null],
|
||||
];
|
||||
}
|
||||
}
|
||||
229
app/Console/Commands/SendRemindersCron.php
Normal file
229
app/Console/Commands/SendRemindersCron.php
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\DataMapper\InvoiceItem;
|
||||
use App\Events\Invoice\InvoiceWasEmailed;
|
||||
use App\Jobs\Entity\EmailEntity;
|
||||
use App\Jobs\Util\WebhookHandler;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Quote;
|
||||
use App\Models\Webhook;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Utils\Traits\MakesReminders;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
//@deprecated 27-11-2022 - only ever should be used for testing
|
||||
class SendRemindersCron extends Command
|
||||
{
|
||||
use MakesReminders, MakesDates;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:send-reminders';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Force send all reminders';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
Invoice::where('next_send_date', '<=', now()->toDateTimeString())
|
||||
->whereNull('deleted_at')
|
||||
->where('is_deleted', 0)
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||
->where('balance', '>', 0)
|
||||
->whereHas('client', function ($query) {
|
||||
$query->where('is_deleted', 0)
|
||||
->where('deleted_at', null);
|
||||
})
|
||||
->whereHas('company', function ($query) {
|
||||
$query->where('is_disabled', 0);
|
||||
})
|
||||
->with('invitations')->cursor()->each(function ($invoice) {
|
||||
if ($invoice->isPayable()) {
|
||||
$reminder_template = $invoice->calculateTemplate('invoice');
|
||||
$invoice->service()->touchReminder($reminder_template)->save();
|
||||
$invoice = $this->calcLateFee($invoice, $reminder_template);
|
||||
|
||||
//check if this reminder needs to be emailed
|
||||
if (in_array($reminder_template, ['reminder1', 'reminder2', 'reminder3']) && $invoice->client->getSetting('enable_'.$reminder_template)) {
|
||||
$invoice->invitations->each(function ($invitation) use ($invoice, $reminder_template) {
|
||||
EmailEntity::dispatch($invitation, $invitation->company, $reminder_template);
|
||||
nlog("Firing reminder email for invoice {$invoice->number}");
|
||||
});
|
||||
|
||||
if ($invoice->invitations->count() > 0) {
|
||||
event(new InvoiceWasEmailed($invoice->invitations->first(), $invoice->company, Ninja::eventVars(), $reminder_template));
|
||||
}
|
||||
}
|
||||
$invoice->service()->setReminder()->save();
|
||||
} else {
|
||||
$invoice->next_send_date = null;
|
||||
$invoice->save();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private function calcLateFee($invoice, $template) :Invoice
|
||||
{
|
||||
$late_fee_amount = 0;
|
||||
$late_fee_percent = 0;
|
||||
|
||||
switch ($template) {
|
||||
case 'reminder1':
|
||||
$late_fee_amount = $invoice->client->getSetting('late_fee_amount1');
|
||||
$late_fee_percent = $invoice->client->getSetting('late_fee_percent1');
|
||||
break;
|
||||
case 'reminder2':
|
||||
$late_fee_amount = $invoice->client->getSetting('late_fee_amount2');
|
||||
$late_fee_percent = $invoice->client->getSetting('late_fee_percent2');
|
||||
break;
|
||||
case 'reminder3':
|
||||
$late_fee_amount = $invoice->client->getSetting('late_fee_amount3');
|
||||
$late_fee_percent = $invoice->client->getSetting('late_fee_percent3');
|
||||
break;
|
||||
case 'endless_reminder':
|
||||
$late_fee_amount = $invoice->client->getSetting('late_fee_endless_amount');
|
||||
$late_fee_percent = $invoice->client->getSetting('late_fee_endless_percent');
|
||||
break;
|
||||
default:
|
||||
$late_fee_amount = 0;
|
||||
$late_fee_percent = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->setLateFee($invoice, $late_fee_amount, $late_fee_percent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the late fee to the invoice line items
|
||||
*
|
||||
* @param Invoice $invoice
|
||||
* @param float $amount The fee amount
|
||||
* @param float $percent The fee percentage amount
|
||||
*
|
||||
* @return Invoice
|
||||
*/
|
||||
private function setLateFee($invoice, $amount, $percent) :Invoice
|
||||
{
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($invoice->client->getMergedSettings()));
|
||||
App::setLocale($invoice->client->locale());
|
||||
|
||||
$temp_invoice_balance = $invoice->balance;
|
||||
|
||||
if ($amount <= 0 && $percent <= 0) {
|
||||
return $invoice;
|
||||
}
|
||||
|
||||
$fee = $amount;
|
||||
|
||||
if ($invoice->partial > 0) {
|
||||
$fee += round($invoice->partial * $percent / 100, 2);
|
||||
} else {
|
||||
$fee += round($invoice->balance * $percent / 100, 2);
|
||||
}
|
||||
|
||||
$invoice_item = new InvoiceItem;
|
||||
$invoice_item->type_id = '5';
|
||||
$invoice_item->product_key = ctrans('texts.fee');
|
||||
$invoice_item->notes = ctrans('texts.late_fee_added', ['date' => $this->translateDate(now()->startOfDay(), $invoice->client->date_format(), $invoice->client->locale())]);
|
||||
$invoice_item->quantity = 1;
|
||||
$invoice_item->cost = $fee;
|
||||
|
||||
$invoice_items = $invoice->line_items;
|
||||
$invoice_items[] = $invoice_item;
|
||||
|
||||
$invoice->line_items = $invoice_items;
|
||||
|
||||
/**Refresh Invoice values*/
|
||||
$invoice->calc()->getInvoice()->save();
|
||||
$invoice->fresh();
|
||||
$invoice->service()->deletePdf()->save();
|
||||
|
||||
/* Refresh the client here to ensure the balance is fresh */
|
||||
$client = $invoice->client;
|
||||
$client = $client->fresh();
|
||||
|
||||
nlog('adjusting client balance and invoice balance by '.($invoice->balance - $temp_invoice_balance));
|
||||
$client->service()->updateBalance($invoice->balance - $temp_invoice_balance)->save();
|
||||
$invoice->ledger()->updateInvoiceBalance($invoice->balance - $temp_invoice_balance, "Late Fee Adjustment for invoice {$invoice->number}");
|
||||
|
||||
return $invoice;
|
||||
}
|
||||
|
||||
private function webHookOverdueInvoices()
|
||||
{
|
||||
if (! config('ninja.db.multi_db_enabled')) {
|
||||
$this->executeWebhooks();
|
||||
} else {
|
||||
//multiDB environment, need to
|
||||
foreach (MultiDB::$dbs as $db) {
|
||||
MultiDB::setDB($db);
|
||||
|
||||
$this->executeWebhooks();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function webHookExpiredQuotes()
|
||||
{
|
||||
}
|
||||
|
||||
private function executeWebhooks()
|
||||
{
|
||||
$invoices = Invoice::where('is_deleted', 0)
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||
->where('balance', '>', 0)
|
||||
->whereDate('due_date', '<=', now()->subDays(1)->startOfDay())
|
||||
->cursor();
|
||||
|
||||
$invoices->each(function ($invoice) {
|
||||
WebhookHandler::dispatch(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company);
|
||||
});
|
||||
|
||||
$quotes = Quote::where('is_deleted', 0)
|
||||
->where('status_id', Quote::STATUS_SENT)
|
||||
->whereDate('due_date', '<=', now()->subDays(1)->startOfDay())
|
||||
->cursor();
|
||||
|
||||
$quotes->each(function ($quote) {
|
||||
WebhookHandler::dispatch(Webhook::EVENT_EXPIRED_QUOTE, $quote, $quote->company);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Ninja\Mailers\ContactMailer as Mailer;
|
||||
use App\Ninja\Repositories\AccountRepository;
|
||||
use Illuminate\Console\Command;
|
||||
use Utils;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Class SendRenewalInvoices.
|
||||
*/
|
||||
class SendRenewalInvoices extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'ninja:send-renewals';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Send renewal invoices';
|
||||
|
||||
/**
|
||||
* @var Mailer
|
||||
*/
|
||||
protected $mailer;
|
||||
|
||||
/**
|
||||
* @var AccountRepository
|
||||
*/
|
||||
protected $accountRepo;
|
||||
|
||||
/**
|
||||
* SendRenewalInvoices constructor.
|
||||
*
|
||||
* @param Mailer $mailer
|
||||
* @param AccountRepository $repo
|
||||
*/
|
||||
public function __construct(Mailer $mailer, AccountRepository $repo)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->mailer = $mailer;
|
||||
$this->accountRepo = $repo;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->info(date('r').' Running SendRenewalInvoices...');
|
||||
|
||||
if ($database = $this->option('database')) {
|
||||
config(['database.default' => $database]);
|
||||
}
|
||||
|
||||
// get all accounts with plans expiring in 10 days
|
||||
$companies = Company::whereRaw("datediff(plan_expires, curdate()) = 10 and (plan = 'pro' or plan = 'enterprise')")
|
||||
->orderBy('id')
|
||||
->get();
|
||||
$this->info($companies->count() . ' companies found renewing in 10 days');
|
||||
|
||||
foreach ($companies as $company) {
|
||||
if (! $company->accounts->count()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$account = $company->accounts->sortBy('id')->first();
|
||||
$plan = [];
|
||||
$plan['plan'] = $company->plan;
|
||||
$plan['term'] = $company->plan_term;
|
||||
$plan['num_users'] = $company->num_users;
|
||||
$plan['price'] = min($company->plan_price, Utils::getPlanPrice($plan));
|
||||
|
||||
if ($plan['plan'] == PLAN_FREE || ! $plan['plan'] || ! $plan['term'] || ! $plan['price']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$client = $this->accountRepo->getNinjaClient($account);
|
||||
$invitation = $this->accountRepo->createNinjaInvoice($client, $account, $plan, 0, false);
|
||||
|
||||
// set the due date to 10 days from now
|
||||
$invoice = $invitation->invoice;
|
||||
$invoice->due_date = date('Y-m-d', strtotime('+ 10 days'));
|
||||
$invoice->save();
|
||||
|
||||
$term = $plan['term'];
|
||||
$plan = $plan['plan'];
|
||||
|
||||
if ($term == PLAN_TERM_YEARLY) {
|
||||
$this->mailer->sendInvoice($invoice);
|
||||
$this->info("Sent {$term}ly {$plan} invoice to {$client->getDisplayName()}");
|
||||
} else {
|
||||
$this->info("Created {$term}ly {$plan} invoice for {$client->getDisplayName()}");
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('Done');
|
||||
|
||||
if ($errorEmail = env('ERROR_EMAIL')) {
|
||||
\Mail::raw('EOM', function ($message) use ($errorEmail, $database) {
|
||||
$message->to($errorEmail)
|
||||
->from(CONTACT_EMAIL)
|
||||
->subject("SendRenewalInvoices [{$database}]: Finished successfully");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getArguments()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['database', null, InputOption::VALUE_OPTIONAL, 'Database', null],
|
||||
];
|
||||
}
|
||||
}
|
||||
93
app/Console/Commands/SendTestEmails.php
Normal file
93
app/Console/Commands/SendTestEmails.php
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\DataMapper\DefaultSettings;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Mail\Migration\MaxCompanies;
|
||||
use App\Models\Account;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use Faker\Factory;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class SendTestEmails extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:send-test-emails';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Sends Test Emails to check templates';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$faker = Factory::create();
|
||||
|
||||
$account = Account::factory()->create();
|
||||
|
||||
$user = User::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'confirmation_code' => '123',
|
||||
'email' => $faker->safeEmail(),
|
||||
'first_name' => 'John',
|
||||
'last_name' => 'Doe',
|
||||
]);
|
||||
|
||||
$company = Company::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
]);
|
||||
|
||||
$user->companies()->attach($company->id, [
|
||||
'account_id' => $account->id,
|
||||
'is_owner' => 1,
|
||||
'is_admin' => 1,
|
||||
'is_locked' => 0,
|
||||
'permissions' => '',
|
||||
'notifications' => CompanySettings::notificationDefaults(),
|
||||
//'settings' => DefaultSettings::userSettings(),
|
||||
'settings' => null,
|
||||
]);
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new MaxCompanies($user->account->companies()->first());
|
||||
$nmo->company = $user->account->companies()->first();
|
||||
$nmo->settings = $user->account->companies()->first()->settings;
|
||||
$nmo->to_user = $user;
|
||||
|
||||
(new NinjaMailerJob($nmo))->handle();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\BankAccountService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Class TestOFX.
|
||||
*/
|
||||
class TestOFX extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'ninja:test-ofx';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Test OFX';
|
||||
|
||||
/**
|
||||
* @var BankAccountService
|
||||
*/
|
||||
protected $bankAccountService;
|
||||
|
||||
/**
|
||||
* TestOFX constructor.
|
||||
*
|
||||
* @param BankAccountService $bankAccountService
|
||||
*/
|
||||
public function __construct(BankAccountService $bankAccountService)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->bankAccountService = $bankAccountService;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->info(date('r').' Running TestOFX...');
|
||||
}
|
||||
}
|
||||
148
app/Console/Commands/TranslationsExport.php
Normal file
148
app/Console/Commands/TranslationsExport.php
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Lang;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class TranslationsExport extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:translations {--type=} {--path=}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Transform translations to json';
|
||||
|
||||
protected $log = '';
|
||||
|
||||
private array $langs = [
|
||||
'ar',
|
||||
'bg',
|
||||
'ca',
|
||||
'cs',
|
||||
'da',
|
||||
'de',
|
||||
'el',
|
||||
'en',
|
||||
'en_GB',
|
||||
'es',
|
||||
'es_ES',
|
||||
'et',
|
||||
'fa',
|
||||
'fi',
|
||||
'fr',
|
||||
'fr_CA',
|
||||
'he',
|
||||
'hr',
|
||||
'it',
|
||||
'ja',
|
||||
'lt',
|
||||
'lv_LV',
|
||||
'mk_MK',
|
||||
'nb_NO',
|
||||
'nl',
|
||||
'pl',
|
||||
'pt_BR',
|
||||
'pt_PT',
|
||||
'ro',
|
||||
'ru_RU',
|
||||
'sl',
|
||||
'sk',
|
||||
'sq',
|
||||
'sr',
|
||||
'sv',
|
||||
'th',
|
||||
'tr_TR',
|
||||
'zh_TW',
|
||||
];
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$type =$this->option('type') ?? 'export';
|
||||
|
||||
if ($type == 'import') {
|
||||
$this->import();
|
||||
}
|
||||
|
||||
if ($type == 'export') {
|
||||
$this->export();
|
||||
}
|
||||
}
|
||||
|
||||
private function import()
|
||||
{
|
||||
//loop and
|
||||
|
||||
foreach ($this->langs as $lang) {
|
||||
$import_file = "textsphp_{$lang}.php";
|
||||
$dir = $this->option('path') ?? storage_path('lang_import/');
|
||||
$path = $dir.$import_file;
|
||||
|
||||
if (file_exists($path)) {
|
||||
$this->logMessage($path);
|
||||
|
||||
$trans = file_get_contents($path);
|
||||
|
||||
file_put_contents(lang_path("/{$lang}/texts.php"), $trans);
|
||||
} else {
|
||||
$this->logMessage("Could not open file");
|
||||
$this->logMessage($path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function export()
|
||||
{
|
||||
Storage::disk('local')->makeDirectory('lang');
|
||||
|
||||
foreach ($this->langs as $lang) {
|
||||
Storage::disk('local')->makeDirectory("lang/{$lang}");
|
||||
|
||||
$translations = Lang::getLoader()->load($lang, 'texts');
|
||||
|
||||
Storage::disk('local')->put("lang/{$lang}/{$lang}.json", json_encode(Arr::dot($translations), JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
|
||||
private function logMessage($str)
|
||||
{
|
||||
$str = date('Y-m-d h:i:s').' '.$str;
|
||||
$this->info($str);
|
||||
$this->log .= $str."\n";
|
||||
}
|
||||
}
|
||||
137
app/Console/Commands/TypeCheck.php
Normal file
137
app/Console/Commands/TypeCheck.php
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Utils\Traits\ClientGroupSettingsSaver;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class TypeCheck extends Command
|
||||
{
|
||||
use ClientGroupSettingsSaver;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:type-check {--all=} {--client_id=} {--company_id=}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Check Settings Types';
|
||||
|
||||
protected $log = '';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
//always return state to first DB
|
||||
|
||||
$current_db = config('database.default');
|
||||
|
||||
if ($this->option('all')) {
|
||||
if (! config('ninja.db.multi_db_enabled')) {
|
||||
$this->checkAll();
|
||||
} else {
|
||||
foreach (MultiDB::$dbs as $db) {
|
||||
MultiDB::setDB($db);
|
||||
|
||||
$this->checkAll();
|
||||
}
|
||||
|
||||
MultiDB::setDB($current_db);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->option('client_id')) {
|
||||
$client = MultiDB::findAndSetDbByClientId($this->option('client_id'));
|
||||
|
||||
if ($client) {
|
||||
$this->checkClient($client);
|
||||
} else {
|
||||
$this->logMessage(date('Y-m-d h:i:s').' Could not find this client');
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->option('company_id')) {
|
||||
$company = MultiDB::findAndSetDbByCompanyId($this->option('company_id'));
|
||||
|
||||
if ($company) {
|
||||
$this->checkCompany($company);
|
||||
} else {
|
||||
$this->logMessage(date('Y-m-d h:i:s').' Could not find this company');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function checkClient($client)
|
||||
{
|
||||
$this->logMessage(date('Y-m-d h:i:s').' Checking Client => '.$client->present()->name().' '.$client->id);
|
||||
|
||||
$entity_settings = $this->checkSettingType($client->settings);
|
||||
$entity_settings->md5 = md5(time());
|
||||
$client->settings = $entity_settings;
|
||||
$client->save();
|
||||
}
|
||||
|
||||
private function checkCompany($company)
|
||||
{
|
||||
$this->logMessage(date('Y-m-d h:i:s').' Checking Company => '.$company->present()->name().' '.$company->id);
|
||||
|
||||
$company->saveSettings((array) $company->settings, $company);
|
||||
}
|
||||
|
||||
private function checkAll()
|
||||
{
|
||||
$this->logMessage(date('Y-m-d h:i:s').' Checking all clients and companies.');
|
||||
|
||||
Client::withTrashed()->cursor()->each(function ($client) {
|
||||
$this->logMessage("Checking client {$client->id}");
|
||||
$entity_settings = $this->checkSettingType($client->settings);
|
||||
$entity_settings->md5 = md5(time());
|
||||
$client->settings = $entity_settings;
|
||||
$client->save();
|
||||
});
|
||||
|
||||
Company::cursor()->each(function ($company) {
|
||||
$this->logMessage("Checking company {$company->id}");
|
||||
$company->saveSettings($company->settings, $company);
|
||||
});
|
||||
}
|
||||
|
||||
private function logMessage($str)
|
||||
{
|
||||
$str = date('Y-m-d h:i:s').' '.$str;
|
||||
$this->info($str);
|
||||
$this->log .= $str."\n";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use App\Models\AccountGateway;
|
||||
use App\Models\BankAccount;
|
||||
use App\Models\User;
|
||||
use Artisan;
|
||||
use Crypt;
|
||||
use Illuminate\Encryption\Encrypter;
|
||||
use Laravel\LegacyEncrypter\McryptEncrypter;
|
||||
|
||||
/**
|
||||
* Class UpdateKey
|
||||
*/
|
||||
class UpdateKey extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'ninja:update-key {--database=} {--key=} {--legacy=}';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Update application key';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->info(date('r') . ' Running UpdateKey...');
|
||||
|
||||
if ($database = $this->option('database')) {
|
||||
config(['database.default' => $database]);
|
||||
}
|
||||
|
||||
if (! env('APP_KEY') || ! env('APP_CIPHER')) {
|
||||
$this->info(date('r') . ' Error: app key and cipher are not set');
|
||||
exit;
|
||||
}
|
||||
|
||||
$legacy = false;
|
||||
if ($this->option('legacy') == 'true') {
|
||||
$legacy = new McryptEncrypter(env('APP_KEY'), env('APP_CIPHER'));
|
||||
}
|
||||
|
||||
// load the current values
|
||||
$gatewayConfigs = [];
|
||||
$bankUsernames = [];
|
||||
$twoFactorSecrets = [];
|
||||
|
||||
foreach (AccountGateway::withTrashed()->get() as $gateway) {
|
||||
if ($legacy) {
|
||||
$gatewayConfigs[$gateway->id] = json_decode($legacy->decrypt($gateway->config));
|
||||
} else {
|
||||
$gatewayConfigs[$gateway->id] = $gateway->getConfig();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (BankAccount::withTrashed()->get() as $bank) {
|
||||
if ($legacy) {
|
||||
$bankUsernames[$bank->id] = $legacy->decrypt($bank->username);
|
||||
} else {
|
||||
$bankUsernames[$bank->id] = $bank->getUsername();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (User::withTrashed()->where('google_2fa_secret', '!=', '')->get() as $user) {
|
||||
if ($legacy) {
|
||||
$twoFactorSecrets[$user->id] = $legacy->decrypt($user->google_2fa_secret);
|
||||
} else {
|
||||
$twoFactorSecrets[$user->id] = Crypt::decrypt($user->google_2fa_secret);
|
||||
}
|
||||
}
|
||||
|
||||
// check if we can write to the .env file
|
||||
$envPath = base_path() . '/.env';
|
||||
$envWriteable = file_exists($envPath) && @fopen($envPath, 'a');
|
||||
|
||||
if ($key = $this->option('key')) {
|
||||
$key = base64_decode(str_replace('base64:', '', $key));
|
||||
} elseif ($envWriteable) {
|
||||
Artisan::call('key:generate');
|
||||
$key = base64_decode(str_replace('base64:', '', config('app.key')));
|
||||
} else {
|
||||
$key = str_random(32);
|
||||
}
|
||||
|
||||
$cipher = $legacy ? 'AES-256-CBC' : config('app.cipher');
|
||||
$crypt = new Encrypter($key, $cipher);
|
||||
|
||||
// update values using the new key/encrypter
|
||||
foreach (AccountGateway::withTrashed()->get() as $gateway) {
|
||||
$config = $gatewayConfigs[$gateway->id];
|
||||
$gateway->config = $crypt->encrypt(json_encode($config));
|
||||
$gateway->save();
|
||||
}
|
||||
|
||||
foreach (BankAccount::withTrashed()->get() as $bank) {
|
||||
$username = $bankUsernames[$bank->id];
|
||||
$bank->username = $crypt->encrypt($username);
|
||||
$bank->save();
|
||||
}
|
||||
|
||||
foreach (User::withTrashed()->where('google_2fa_secret', '!=', '')->get() as $user) {
|
||||
$secret = $twoFactorSecrets[$user->id];
|
||||
$user->google_2fa_secret = $crypt->encrypt($secret);
|
||||
$user->save();
|
||||
}
|
||||
|
||||
$message = date('r') . ' Successfully updated ';
|
||||
if ($envWriteable) {
|
||||
if ($legacy) {
|
||||
$message .= 'the key, set the cipher in the .env file to AES-256-CBC';
|
||||
} else {
|
||||
$message .= 'the key';
|
||||
}
|
||||
} else {
|
||||
if ($legacy) {
|
||||
$message .= 'the data, make sure to set the new cipher/key: AES-256-CBC/' . $key;
|
||||
} else {
|
||||
$message .= 'the data, make sure to set the new key: ' . $key;
|
||||
}
|
||||
}
|
||||
$this->info($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getArguments()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['legacy', null, InputOption::VALUE_OPTIONAL, 'Legacy', null],
|
||||
['database', null, InputOption::VALUE_OPTIONAL, 'Database', null],
|
||||
['key', null, InputOption::VALUE_OPTIONAL, 'Key', null],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use App\Http\Controllers\BaseAPIController;
|
||||
use Modules\$STUDLY_NAME$\Repositories\$STUDLY_NAME$Repository;
|
||||
use Modules\$STUDLY_NAME$\Http\Requests\$STUDLY_NAME$Request;
|
||||
use Modules\$STUDLY_NAME$\Http\Requests\Create$STUDLY_NAME$Request;
|
||||
use Modules\$STUDLY_NAME$\Http\Requests\Update$STUDLY_NAME$Request;
|
||||
|
||||
class $STUDLY_NAME$ApiController extends BaseAPIController
|
||||
{
|
||||
protected $$STUDLY_NAME$Repo;
|
||||
protected $entityType = '$LOWER_NAME$';
|
||||
|
||||
public function __construct($STUDLY_NAME$Repository $$LOWER_NAME$Repo)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->$LOWER_NAME$Repo = $$LOWER_NAME$Repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @SWG\Get(
|
||||
* path="/$LOWER_NAME$",
|
||||
* summary="List $LOWER_NAME$",
|
||||
* operationId="list$STUDLY_NAME$s",
|
||||
* tags={"$LOWER_NAME$"},
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="A list of $LOWER_NAME$",
|
||||
* @SWG\Schema(type="array", @SWG\Items(ref="#/definitions/$STUDLY_NAME$"))
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response="default",
|
||||
* description="an ""unexpected"" error"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$data = $this->$LOWER_NAME$Repo->all();
|
||||
|
||||
return $this->listResponse($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @SWG\Get(
|
||||
* path="/$LOWER_NAME$/{$LOWER_NAME$_id}",
|
||||
* summary="Individual $STUDLY_NAME$",
|
||||
* operationId="get$STUDLY_NAME$",
|
||||
* tags={"$LOWER_NAME$"},
|
||||
* @SWG\Parameter(
|
||||
* in="path",
|
||||
* name="$LOWER_NAME$_id",
|
||||
* type="integer",
|
||||
* required=true
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="A single $LOWER_NAME$",
|
||||
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/$STUDLY_NAME$"))
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response="default",
|
||||
* description="an ""unexpected"" error"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function show($STUDLY_NAME$Request $request)
|
||||
{
|
||||
return $this->itemResponse($request->entity());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @SWG\Post(
|
||||
* path="/$LOWER_NAME$",
|
||||
* summary="Create a $LOWER_NAME$",
|
||||
* operationId="create$STUDLY_NAME$",
|
||||
* tags={"$LOWER_NAME$"},
|
||||
* @SWG\Parameter(
|
||||
* in="body",
|
||||
* name="$LOWER_NAME$",
|
||||
* @SWG\Schema(ref="#/definitions/$STUDLY_NAME$")
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="New $LOWER_NAME$",
|
||||
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/$STUDLY_NAME$"))
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response="default",
|
||||
* description="an ""unexpected"" error"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function store(Create$STUDLY_NAME$Request $request)
|
||||
{
|
||||
$$LOWER_NAME$ = $this->$LOWER_NAME$Repo->save($request->input());
|
||||
|
||||
return $this->itemResponse($$LOWER_NAME$);
|
||||
}
|
||||
|
||||
/**
|
||||
* @SWG\Put(
|
||||
* path="/$LOWER_NAME$/{$LOWER_NAME$_id}",
|
||||
* summary="Update a $LOWER_NAME$",
|
||||
* operationId="update$STUDLY_NAME$",
|
||||
* tags={"$LOWER_NAME$"},
|
||||
* @SWG\Parameter(
|
||||
* in="path",
|
||||
* name="$LOWER_NAME$_id",
|
||||
* type="integer",
|
||||
* required=true
|
||||
* ),
|
||||
* @SWG\Parameter(
|
||||
* in="body",
|
||||
* name="$LOWER_NAME$",
|
||||
* @SWG\Schema(ref="#/definitions/$STUDLY_NAME$")
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="Updated $LOWER_NAME$",
|
||||
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/$STUDLY_NAME$"))
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response="default",
|
||||
* description="an ""unexpected"" error"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function update(Update$STUDLY_NAME$Request $request, $publicId)
|
||||
{
|
||||
if ($request->action) {
|
||||
return $this->handleAction($request);
|
||||
}
|
||||
|
||||
$$LOWER_NAME$ = $this->$LOWER_NAME$Repo->save($request->input(), $request->entity());
|
||||
|
||||
return $this->itemResponse($$LOWER_NAME$);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @SWG\Delete(
|
||||
* path="/$LOWER_NAME$/{$LOWER_NAME$_id}",
|
||||
* summary="Delete a $LOWER_NAME$",
|
||||
* operationId="delete$STUDLY_NAME$",
|
||||
* tags={"$LOWER_NAME$"},
|
||||
* @SWG\Parameter(
|
||||
* in="path",
|
||||
* name="$LOWER_NAME$_id",
|
||||
* type="integer",
|
||||
* required=true
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="Deleted $LOWER_NAME$",
|
||||
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/$STUDLY_NAME$"))
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response="default",
|
||||
* description="an ""unexpected"" error"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function destroy(Update$STUDLY_NAME$Request $request)
|
||||
{
|
||||
$$LOWER_NAME$ = $request->entity();
|
||||
|
||||
$this->$LOWER_NAME$Repo->delete($$LOWER_NAME$);
|
||||
|
||||
return $this->itemResponse($$LOWER_NAME$);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use App\Providers\AuthServiceProvider;
|
||||
|
||||
class $STUDLY_NAME$AuthProvider extends AuthServiceProvider
|
||||
{
|
||||
/**
|
||||
* The policy mappings for the application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $policies = [
|
||||
\Modules\$STUDLY_NAME$\Models\$STUDLY_NAME$::class => \Modules\$STUDLY_NAME$\Policies\$STUDLY_NAME$Policy::class,
|
||||
];
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
||||
class $CLASS$ extends Command
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = '$COMMAND_NAME$';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Command description.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getArguments()
|
||||
{
|
||||
return [
|
||||
['example', InputArgument::REQUIRED, 'An example argument.'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [
|
||||
['example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"name": "$VENDOR$/$LOWER_NAME$",
|
||||
"description": "",
|
||||
"authors": [
|
||||
{
|
||||
"name": "$AUTHOR_NAME$",
|
||||
"email": "$AUTHOR_EMAIL$"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"$MODULE_NAMESPACE$\\$STUDLY_NAME$\\": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $CLASS_NAMESPACE$;
|
||||
|
||||
use Illuminate\Routing\Controller;
|
||||
|
||||
class $CLASS$ extends Controller
|
||||
{
|
||||
}
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $CLASS_NAMESPACE$;
|
||||
|
||||
use Auth;
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Services\DatatableService;
|
||||
use Modules\$STUDLY_NAME$\Datatables\$STUDLY_NAME$Datatable;
|
||||
use Modules\$STUDLY_NAME$\Repositories\$STUDLY_NAME$Repository;
|
||||
use Modules\$STUDLY_NAME$\Http\Requests\$STUDLY_NAME$Request;
|
||||
use Modules\$STUDLY_NAME$\Http\Requests\Create$STUDLY_NAME$Request;
|
||||
use Modules\$STUDLY_NAME$\Http\Requests\Update$STUDLY_NAME$Request;
|
||||
|
||||
class $CLASS$ extends BaseController
|
||||
{
|
||||
protected $$STUDLY_NAME$Repo;
|
||||
//protected $entityType = '$LOWER_NAME$';
|
||||
|
||||
public function __construct($STUDLY_NAME$Repository $$LOWER_NAME$Repo)
|
||||
{
|
||||
//parent::__construct();
|
||||
|
||||
$this->$LOWER_NAME$Repo = $$LOWER_NAME$Repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
* @return Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('list_wrapper', [
|
||||
'entityType' => '$LOWER_NAME$',
|
||||
'datatable' => new $STUDLY_NAME$Datatable(),
|
||||
'title' => mtrans('$LOWER_NAME$', '$LOWER_NAME$_list'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function datatable(DatatableService $datatableService)
|
||||
{
|
||||
$search = request()->input('sSearch');
|
||||
$userId = Auth::user()->filterId();
|
||||
|
||||
$datatable = new $STUDLY_NAME$Datatable();
|
||||
$query = $this->$LOWER_NAME$Repo->find($search, $userId);
|
||||
|
||||
return $datatableService->createDatatable($datatable, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
* @return Response
|
||||
*/
|
||||
public function create($STUDLY_NAME$Request $request)
|
||||
{
|
||||
$data = [
|
||||
'$LOWER_NAME$' => null,
|
||||
'method' => 'POST',
|
||||
'url' => '$LOWER_NAME$',
|
||||
'title' => mtrans('$LOWER_NAME$', 'new_$LOWER_NAME$'),
|
||||
];
|
||||
|
||||
return view('$LOWER_NAME$::edit', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function store(Create$STUDLY_NAME$Request $request)
|
||||
{
|
||||
$$LOWER_NAME$ = $this->$LOWER_NAME$Repo->save($request->input());
|
||||
|
||||
return redirect()->to($$LOWER_NAME$->present()->editUrl)
|
||||
->with('message', mtrans('$LOWER_NAME$', 'created_$LOWER_NAME$'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
* @return Response
|
||||
*/
|
||||
public function edit($STUDLY_NAME$Request $request)
|
||||
{
|
||||
$$LOWER_NAME$ = $request->entity();
|
||||
|
||||
$data = [
|
||||
'$LOWER_NAME$' => $$LOWER_NAME$,
|
||||
'method' => 'PUT',
|
||||
'url' => '$LOWER_NAME$/' . $$LOWER_NAME$->public_id,
|
||||
'title' => mtrans('$LOWER_NAME$', 'edit_$LOWER_NAME$'),
|
||||
];
|
||||
|
||||
return view('$LOWER_NAME$::edit', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing a resource.
|
||||
* @return Response
|
||||
*/
|
||||
public function show($STUDLY_NAME$Request $request)
|
||||
{
|
||||
return redirect()->to("$LOWER_NAME$/{$request->$LOWER_NAME$}/edit");
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function update(Update$STUDLY_NAME$Request $request)
|
||||
{
|
||||
$$LOWER_NAME$ = $this->$LOWER_NAME$Repo->save($request->input(), $request->entity());
|
||||
|
||||
return redirect()->to($$LOWER_NAME$->present()->editUrl)
|
||||
->with('message', mtrans('$LOWER_NAME$', 'updated_$LOWER_NAME$'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update multiple resources
|
||||
*/
|
||||
public function bulk()
|
||||
{
|
||||
$action = request()->input('action');
|
||||
$ids = request()->input('public_id') ?: request()->input('ids');
|
||||
$count = $this->$LOWER_NAME$Repo->bulk($ids, $action);
|
||||
|
||||
return redirect()->to('$LOWER_NAME$')
|
||||
->with('message', mtrans('$LOWER_NAME$', $action . '_$LOWER_NAME$_complete'));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
class Create$CLASS$Request extends $CLASS$Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return $this->user()->can('create', '$LOWER_NAME$');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use Utils;
|
||||
use URL;
|
||||
use Auth;
|
||||
use App\Ninja\Datatables\EntityDatatable;
|
||||
|
||||
class $CLASS$Datatable extends EntityDatatable
|
||||
{
|
||||
public $entityType = '$LOWER_NAME$';
|
||||
public $sortCol = 1;
|
||||
|
||||
public function columns()
|
||||
{
|
||||
return [
|
||||
$DATATABLE_COLUMNS$
|
||||
[
|
||||
'created_at',
|
||||
function ($model) {
|
||||
return Utils::fromSqlDateTime($model->created_at);
|
||||
}
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function actions()
|
||||
{
|
||||
return [
|
||||
[
|
||||
mtrans('$LOWER_NAME$', 'edit_$LOWER_NAME$'),
|
||||
function ($model) {
|
||||
return URL::to("$LOWER_NAME$/{$model->public_id}/edit");
|
||||
},
|
||||
function ($model) {
|
||||
return Auth::user()->can('editByOwner', ['$LOWER_NAME$', $model->user_id]);
|
||||
}
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class $CLASS$
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the channels the event should be broadcast on.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function broadcastOn()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Bus\Queueable;
|
||||
|
||||
class $CLASS$ implements ShouldQueue
|
||||
{
|
||||
use InteractsWithQueue, SerializesModels, Queueable;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"name": "$STUDLY_NAME$",
|
||||
"alias": "$LOWER_NAME$",
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
"active": 1,
|
||||
"order": 0,
|
||||
"providers": [
|
||||
"$MODULE_NAMESPACE$\\$STUDLY_NAME$\\Providers\\$STUDLY_NAME$ServiceProvider"
|
||||
],
|
||||
"aliases":{},
|
||||
"files": [
|
||||
"start.php"
|
||||
],
|
||||
"icon": "th-large",
|
||||
"plural": "$LOWER_NAME$"
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
|
||||
$LANG = array(
|
||||
|
||||
'$LOWER_NAME$' => '$STUDLY_NAME$',
|
||||
'$LOWER_NAME$_list' => '$STUDLY_NAME$ List',
|
||||
'archive_$LOWER_NAME$' => 'Archive $STUDLY_NAME$',
|
||||
'delete_$LOWER_NAME$' => 'Delete $STUDLY_NAME$',
|
||||
'edit_$LOWER_NAME$' => 'Edit $STUDLY_NAME$',
|
||||
'restore_$LOWER_NAME$' => 'Restore $STUDLY_NAME$',
|
||||
'new_$LOWER_NAME$' => 'New $STUDLY_NAME$',
|
||||
'created_$LOWER_NAME$' => 'Successfully created $LOWER_NAME$',
|
||||
'updated_$LOWER_NAME$' => 'Successfully updated $LOWER_NAME$',
|
||||
|
||||
);
|
||||
|
||||
return $LANG;
|
||||
|
||||
?>
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use $EVENTNAME$;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
class $CLASS$
|
||||
{
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param \$EVENTNAME$ $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle(\$EVENTNAME$ $event)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
class $CLASS$ extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return $this->view('view.name');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class $CLASS$
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class $CLASS$ extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('$TABLE$', function (Blueprint $table) {
|
||||
$FIELDS_UP$
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('$TABLE$', function (Blueprint $table) {
|
||||
$FIELDS_DOWN$
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class $CLASS$ extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create(strtolower('$TABLE$'), function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->unsignedInteger('user_id')->index();
|
||||
$table->unsignedInteger('account_id')->index();
|
||||
$table->unsignedInteger('client_id')->index()->nullable();
|
||||
|
||||
$FIELDS$
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
$table->boolean('is_deleted')->default(false);
|
||||
|
||||
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
|
||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||
$table->foreign('client_id')->references('id')->on('clients')->onDelete('cascade');
|
||||
|
||||
$table->unsignedInteger('public_id')->index();
|
||||
$table->unique( ['account_id', 'public_id'] );
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists(strtolower('$TABLE$'));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class $CLASS$ extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('$TABLE$', function (Blueprint $table) {
|
||||
$FIELDS_UP$
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('$TABLE$', function (Blueprint $table) {
|
||||
$FIELDS_DOWN$
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class $CLASS$ extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::dropIfExists('$TABLE$');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::create('$TABLE$', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$FIELDS$
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class $CLASS$ extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use App\Models\EntityModel;
|
||||
use Laracasts\Presenter\PresentableTrait;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class $CLASS$ extends EntityModel
|
||||
{
|
||||
use PresentableTrait;
|
||||
use SoftDeletes;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $presenter = 'Modules\$CLASS$\Presenters\$CLASS$Presenter';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fillable = $FILLABLE$;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $table = '$LOWER_NAME$';
|
||||
|
||||
public function getEntityType()
|
||||
{
|
||||
return '$LOWER_NAME$';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
|
||||
class $CLASS$ extends Notification
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function via($notifiable)
|
||||
{
|
||||
return ['mail'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||
*/
|
||||
public function toMail($notifiable)
|
||||
{
|
||||
return (new MailMessage)
|
||||
->line('The introduction to the notification.')
|
||||
->action('Notification Action', 'https://laravel.com')
|
||||
->line('Thank you for using our application!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($notifiable)
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use App\Policies\EntityPolicy;
|
||||
|
||||
class $STUDLY_NAME$Policy extends EntityPolicy
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use App\Ninja\Presenters\EntityPresenter;
|
||||
|
||||
class $STUDLY_NAME$Presenter extends EntityPresenter
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use App\Providers\AuthServiceProvider;
|
||||
|
||||
class $CLASS$ extends AuthServiceProvider
|
||||
{
|
||||
/**
|
||||
* Indicates if loading of the provider is deferred.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $defer = false;
|
||||
|
||||
/**
|
||||
* The policy mappings for the application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $policies = [
|
||||
\Modules\$STUDLY_NAME$\Models\$STUDLY_NAME$::class => \Modules\$STUDLY_NAME$\Policies\$STUDLY_NAME$Policy::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provides()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use DB;
|
||||
use Modules\$STUDLY_NAME$\Models\$STUDLY_NAME$;
|
||||
use App\Ninja\Repositories\BaseRepository;
|
||||
//use App\Events\$STUDLY_NAME$WasCreated;
|
||||
//use App\Events\$STUDLY_NAME$WasUpdated;
|
||||
|
||||
class $STUDLY_NAME$Repository extends BaseRepository
|
||||
{
|
||||
public function getClassName()
|
||||
{
|
||||
return 'Modules\$STUDLY_NAME$\Models\$STUDLY_NAME$';
|
||||
}
|
||||
|
||||
public function all()
|
||||
{
|
||||
return $STUDLY_NAME$::scope()
|
||||
->orderBy('created_at', 'desc')
|
||||
->withTrashed();
|
||||
}
|
||||
|
||||
public function find($filter = null, $userId = false)
|
||||
{
|
||||
$query = DB::table('$LOWER_NAME$')
|
||||
->where('$LOWER_NAME$.account_id', '=', \Auth::user()->account_id)
|
||||
->select(
|
||||
$DATABASE_FIELDS$
|
||||
'$LOWER_NAME$.public_id',
|
||||
'$LOWER_NAME$.deleted_at',
|
||||
'$LOWER_NAME$.created_at',
|
||||
'$LOWER_NAME$.is_deleted',
|
||||
'$LOWER_NAME$.user_id'
|
||||
);
|
||||
|
||||
$this->applyFilters($query, '$LOWER_NAME$');
|
||||
|
||||
if ($userId) {
|
||||
$query->where('clients.user_id', '=', $userId);
|
||||
}
|
||||
|
||||
/*
|
||||
if ($filter) {
|
||||
$query->where();
|
||||
}
|
||||
*/
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function save($data, $$LOWER_NAME$ = null)
|
||||
{
|
||||
$entity = $$LOWER_NAME$ ?: $STUDLY_NAME$::createNew();
|
||||
|
||||
$entity->fill($data);
|
||||
$entity->save();
|
||||
|
||||
/*
|
||||
if (!$publicId || intval($publicId) < 0) {
|
||||
event(new ClientWasCreated($client));
|
||||
} else {
|
||||
event(new ClientWasUpdated($client));
|
||||
}
|
||||
*/
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use App\Http\Requests\EntityRequest;
|
||||
|
||||
class $CLASS$Request extends EntityRequest
|
||||
{
|
||||
protected $entityType = '$LOWER_NAME$';
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $MODULE_NAMESPACE$\$MODULE$\Providers;
|
||||
|
||||
use Illuminate\Routing\Router;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
|
||||
class $NAME$ extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The root namespace to assume when generating URLs to actions.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rootUrlNamespace = '$MODULE_NAMESPACE$\$MODULE$\Http\Controllers';
|
||||
|
||||
/**
|
||||
* Called before routes are registered.
|
||||
*
|
||||
* Register any model bindings or pattern based filters.
|
||||
*
|
||||
* @param Router $router
|
||||
* @return void
|
||||
*/
|
||||
public function before(Router $router)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the routes for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function map(Router $router)
|
||||
{
|
||||
// require __DIR__ . '/../Http/routes.php';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
Route::group(['middleware' => ['web', 'lookup:user', 'auth:user'], 'namespace' => '$MODULE_NAMESPACE$\$STUDLY_NAME$\Http\Controllers'], function()
|
||||
{
|
||||
Route::resource('$LOWER_NAME$', '$STUDLY_NAME$Controller');
|
||||
Route::post('$LOWER_NAME$/bulk', '$STUDLY_NAME$Controller@bulk');
|
||||
Route::get('api/$LOWER_NAME$', '$STUDLY_NAME$Controller@datatable');
|
||||
});
|
||||
|
||||
Route::group(['middleware' => 'api', 'namespace' => '$MODULE_NAMESPACE$\$STUDLY_NAME$\Http\ApiControllers', 'prefix' => 'api/v1'], function()
|
||||
{
|
||||
Route::resource('$LOWER_NAME$', '$STUDLY_NAME$ApiController');
|
||||
});
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'name' => '$STUDLY_NAME$'
|
||||
];
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use App\Providers\AuthServiceProvider;
|
||||
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
|
||||
|
||||
class $CLASS$ extends AuthServiceProvider
|
||||
{
|
||||
/**
|
||||
* Indicates if loading of the provider is deferred.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $defer = false;
|
||||
|
||||
/**
|
||||
* The policy mappings for the application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $policies = [
|
||||
\Modules\$STUDLY_NAME$\Models\$STUDLY_NAME$::class => \Modules\$STUDLY_NAME$\Policies\$STUDLY_NAME$Policy::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Boot the application events.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
$this->registerTranslations();
|
||||
$this->registerConfig();
|
||||
$this->registerViews();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Register config.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function registerConfig()
|
||||
{
|
||||
$this->publishes([
|
||||
__DIR__.'/../$PATH_CONFIG$/config.php' => config_path('$LOWER_NAME$.php'),
|
||||
]);
|
||||
$this->mergeConfigFrom(
|
||||
__DIR__.'/../$PATH_CONFIG$/config.php', '$LOWER_NAME$'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register views.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function registerViews()
|
||||
{
|
||||
$viewPath = base_path('resources/views/modules/$LOWER_NAME$');
|
||||
|
||||
$sourcePath = __DIR__.'/../$PATH_VIEWS$';
|
||||
|
||||
$this->publishes([
|
||||
$sourcePath => $viewPath
|
||||
]);
|
||||
|
||||
$this->loadViewsFrom(array_merge(array_map(function ($path) {
|
||||
return $path . '/modules/$LOWER_NAME$';
|
||||
}, \Config::get('view.paths')), [$sourcePath]), '$LOWER_NAME$');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register translations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function registerTranslations()
|
||||
{
|
||||
$langPath = base_path('resources/lang/modules/$LOWER_NAME$');
|
||||
|
||||
if (is_dir($langPath)) {
|
||||
$this->loadTranslationsFrom($langPath, '$LOWER_NAME$');
|
||||
} else {
|
||||
$this->loadTranslationsFrom(__DIR__ .'/../$PATH_LANG$', '$LOWER_NAME$');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provides()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$\Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class $NAME$ extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
Model::unguard();
|
||||
|
||||
// $this->call("OthersTableSeeder");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register Namespaces And Routes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When a module starting, this file will executed automatically. This helps
|
||||
| to register some namespaces like translator or view. Also this file
|
||||
| will load the routes file for each module. You may also modify
|
||||
| this file as you want.
|
||||
|
|
||||
*/
|
||||
|
||||
require __DIR__ . '/Http/routes.php';
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use Modules\$STUDLY_NAME$\Models\$STUDLY_NAME$;
|
||||
use App\Ninja\Transformers\EntityTransformer;
|
||||
|
||||
/**
|
||||
* @SWG\Definition(definition="$STUDLY_NAME$", @SWG\Xml(name="$STUDLY_NAME$"))
|
||||
*/
|
||||
|
||||
class $STUDLY_NAME$Transformer extends EntityTransformer
|
||||
{
|
||||
/**
|
||||
* @SWG\Property(property="id", type="integer", example=1, readOnly=true)
|
||||
* @SWG\Property(property="user_id", type="integer", example=1)
|
||||
* @SWG\Property(property="account_key", type="string", example="123456")
|
||||
* @SWG\Property(property="updated_at", type="integer", example=1451160233, readOnly=true)
|
||||
* @SWG\Property(property="archived_at", type="integer", example=1451160233, readOnly=true)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param $STUDLY_NAME$ $$LOWER_NAME$
|
||||
* @return array
|
||||
*/
|
||||
public function transform($STUDLY_NAME$ $$LOWER_NAME$)
|
||||
{
|
||||
return array_merge($this->getDefaults($$LOWER_NAME$), [
|
||||
$TRANSFORMER_FIELDS$
|
||||
'id' => (int) $$LOWER_NAME$->public_id,
|
||||
'updated_at' => $this->getTimestamp($$LOWER_NAME$->updated_at),
|
||||
'archived_at' => $this->getTimestamp($$LOWER_NAME$->deleted_at),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
class Update$CLASS$Request extends $CLASS$Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return $this->user()->can('edit', $this->entity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
@extends('header')
|
||||
|
||||
@section('content')
|
||||
|
||||
{!! Former::open($url)
|
||||
->addClass('col-md-10 col-md-offset-1 warn-on-exit')
|
||||
->method($method)
|
||||
->rules([]) !!}
|
||||
|
||||
@if ($$LOWER_NAME$)
|
||||
{!! Former::populate($$LOWER_NAME$) !!}
|
||||
<div style="display:none">
|
||||
{!! Former::text('public_id') !!}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
|
||||
$FORM_FIELDS$
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<center class="buttons">
|
||||
|
||||
{!! Button::normal(trans('texts.cancel'))
|
||||
->large()
|
||||
->asLinkTo(URL::to('/$LOWER_NAME$'))
|
||||
->appendIcon(Icon::create('remove-circle')) !!}
|
||||
|
||||
{!! Button::success(trans('texts.save'))
|
||||
->submit()
|
||||
->large()
|
||||
->appendIcon(Icon::create('floppy-disk')) !!}
|
||||
|
||||
</center>
|
||||
|
||||
{!! Former::close() !!}
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
$(function() {
|
||||
$(".warn-on-exit input").first().focus();
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@stop
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Module $STUDLY_NAME$</title>
|
||||
</head>
|
||||
<body>
|
||||
@yield('content')
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,59 +1,138 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use App\Jobs\Cron\AutoBillCron;
|
||||
use App\Jobs\Cron\RecurringExpensesCron;
|
||||
use App\Jobs\Cron\RecurringInvoicesCron;
|
||||
use App\Jobs\Cron\SubscriptionCron;
|
||||
use App\Jobs\Invoice\InvoiceCheckLateWebhook;
|
||||
use App\Jobs\Ninja\AdjustEmailQuota;
|
||||
use App\Jobs\Ninja\BankTransactionSync;
|
||||
use App\Jobs\Ninja\CompanySizeCheck;
|
||||
use App\Jobs\Ninja\QueueSize;
|
||||
use App\Jobs\Ninja\SystemMaintenance;
|
||||
use App\Jobs\Ninja\TaskScheduler;
|
||||
use App\Jobs\Quote\QuoteCheckExpired;
|
||||
use App\Jobs\Subscription\CleanStaleInvoiceOrder;
|
||||
use App\Jobs\Util\DiskCleanup;
|
||||
use App\Jobs\Util\ReminderJob;
|
||||
use App\Jobs\Util\SchedulerCheck;
|
||||
use App\Jobs\Util\SendFailedEmails;
|
||||
use App\Jobs\Util\UpdateExchangeRates;
|
||||
use App\Jobs\Util\VersionCheck;
|
||||
use App\Models\Account;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
use Utils;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
/**
|
||||
* The Artisan commands provided by your application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $commands = [
|
||||
'App\Console\Commands\SendRecurringInvoices',
|
||||
'App\Console\Commands\RemoveOrphanedDocuments',
|
||||
'App\Console\Commands\ResetData',
|
||||
'App\Console\Commands\CheckData',
|
||||
'App\Console\Commands\PruneData',
|
||||
'App\Console\Commands\CreateTestData',
|
||||
'App\Console\Commands\CreateLuisData',
|
||||
'App\Console\Commands\MobileLocalization',
|
||||
'App\Console\Commands\SendRenewalInvoices',
|
||||
'App\Console\Commands\ChargeRenewalInvoices',
|
||||
'App\Console\Commands\SendReminders',
|
||||
'App\Console\Commands\TestOFX',
|
||||
'App\Console\Commands\MakeModule',
|
||||
'App\Console\Commands\MakeClass',
|
||||
'App\Console\Commands\InitLookup',
|
||||
'App\Console\Commands\CalculatePayouts',
|
||||
'App\Console\Commands\UpdateKey',
|
||||
'App\Console\Commands\ExportMigrations',
|
||||
];
|
||||
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*
|
||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
||||
*
|
||||
* @param Schedule $schedule
|
||||
* @return void
|
||||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
$logFile = storage_path() . '/logs/cron.log';
|
||||
/* Check for the latest version of Invoice Ninja */
|
||||
$schedule->job(new VersionCheck)->daily();
|
||||
|
||||
$schedule
|
||||
->command('ninja:send-invoices --force')
|
||||
->sendOutputTo($logFile)
|
||||
->withoutOverlapping()
|
||||
->hourly();
|
||||
/* Checks and cleans redundant files */
|
||||
$schedule->job(new DiskCleanup)->dailyAt('02:10')->withoutOverlapping()->name('disk-cleanup-job')->onOneServer();
|
||||
|
||||
$schedule
|
||||
->command('ninja:send-reminders --force')
|
||||
->sendOutputTo($logFile)
|
||||
->daily();
|
||||
/* Send reminders */
|
||||
$schedule->job(new ReminderJob)->hourly()->withoutOverlapping()->name('reminder-job')->onOneServer();
|
||||
|
||||
/* Returns the number of jobs in the queue */
|
||||
$schedule->job(new QueueSize)->everyFiveMinutes()->withoutOverlapping()->name('queue-size-job')->onOneServer();
|
||||
|
||||
/* Checks for large companies and marked them as is_large */
|
||||
$schedule->job(new CompanySizeCheck)->dailyAt('23:20')->withoutOverlapping()->name('company-size-job')->onOneServer();
|
||||
|
||||
/* Pulls in the latest exchange rates */
|
||||
$schedule->job(new UpdateExchangeRates)->dailyAt('23:30')->withoutOverlapping()->name('exchange-rate-job')->onOneServer();
|
||||
|
||||
/* Runs cleanup code for subscriptions */
|
||||
$schedule->job(new SubscriptionCron)->dailyAt('00:01')->withoutOverlapping()->name('subscription-job')->onOneServer();
|
||||
|
||||
/* Sends recurring invoices*/
|
||||
$schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping()->name('recurring-invoice-job')->onOneServer();
|
||||
|
||||
/* Stale Invoice Cleanup*/
|
||||
$schedule->job(new CleanStaleInvoiceOrder)->hourlyAt(30)->withoutOverlapping()->name('stale-invoice-job')->onOneServer();
|
||||
|
||||
/* Sends recurring invoices*/
|
||||
$schedule->job(new RecurringExpensesCron)->dailyAt('00:10')->withoutOverlapping()->name('recurring-expense-job')->onOneServer();
|
||||
|
||||
/* Fires notifications for expired Quotes */
|
||||
$schedule->job(new QuoteCheckExpired)->dailyAt('05:10')->withoutOverlapping()->name('quote-expired-job')->onOneServer();
|
||||
|
||||
/* Fires webhooks for overdue Invoice */
|
||||
$schedule->job(new InvoiceCheckLateWebhook)->dailyAt('07:00')->withoutOverlapping()->name('invoice-overdue-job')->onOneServer();
|
||||
|
||||
/* Performs auto billing */
|
||||
$schedule->job(new AutoBillCron)->dailyAt('06:20')->withoutOverlapping()->name('auto-bill-job')->onOneServer();
|
||||
|
||||
/* Checks the status of the scheduler */
|
||||
$schedule->job(new SchedulerCheck)->dailyAt('01:10')->withoutOverlapping();
|
||||
|
||||
/* Checks for scheduled tasks */
|
||||
$schedule->job(new TaskScheduler())->hourlyAt(10)->withoutOverlapping()->name('task-scheduler-job')->onOneServer();
|
||||
|
||||
/* Performs system maintenance such as pruning the backup table */
|
||||
$schedule->job(new SystemMaintenance)->sundays()->at('02:30')->withoutOverlapping()->name('system-maintenance-job')->onOneServer();
|
||||
|
||||
|
||||
if (Ninja::isSelfHost()) {
|
||||
$schedule->call(function () {
|
||||
Account::whereNotNull('id')->update(['is_scheduler_running' => true]);
|
||||
})->everyFiveMinutes();
|
||||
}
|
||||
|
||||
/* Run hosted specific jobs */
|
||||
if (Ninja::isHosted()) {
|
||||
$schedule->job(new AdjustEmailQuota)->dailyAt('23:30')->withoutOverlapping();
|
||||
|
||||
/* Pulls in bank transactions from third party services */
|
||||
$schedule->job(new BankTransactionSync)->dailyAt('04:10')->withoutOverlapping()->name('bank-trans-sync-job')->onOneServer();
|
||||
|
||||
//not used @deprecate
|
||||
// $schedule->job(new SendFailedEmails)->daily()->withoutOverlapping();
|
||||
|
||||
$schedule->command('ninja:check-data --database=db-ninja-01')->dailyAt('02:10')->withoutOverlapping()->name('check-data-db-1-job')->onOneServer();
|
||||
|
||||
$schedule->command('ninja:check-data --database=db-ninja-02')->dailyAt('02:20')->withoutOverlapping()->name('check-data-db-2-job')->onOneServer();
|
||||
|
||||
$schedule->command('ninja:s3-cleanup')->dailyAt('23:15')->withoutOverlapping()->name('s3-cleanup-job')->onOneServer();
|
||||
}
|
||||
|
||||
if (config('queue.default') == 'database' && Ninja::isSelfHost() && config('ninja.internal_queue_enabled') && ! config('ninja.is_docker')) {
|
||||
$schedule->command('queue:work database --stop-when-empty --memory=256')->everyMinute()->withoutOverlapping();
|
||||
|
||||
$schedule->command('queue:restart')->everyFiveMinutes()->withoutOverlapping();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the commands for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function commands()
|
||||
{
|
||||
$this->load(__DIR__.'/Commands');
|
||||
|
||||
require base_path('routes/console.php');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue