Compare commits
187 Commits
Author | SHA1 | Date | |
---|---|---|---|
3c7e2bbbc9 | |||
844d509834 | |||
f458bb19c5 | |||
76d43a81c8 | |||
b3d5bf05a9 | |||
f0ec35f463 | |||
326b1dcfc5 | |||
b6b036e06d | |||
e7ac329d24 | |||
648d941893 | |||
59dc825bf7 | |||
1b4504cee2 | |||
74864e66cb | |||
c0cffc3e2c | |||
4e450d4efc | |||
1374d38545 | |||
7c91082ca8 | |||
27720ee882 | |||
8f283f83f2 | |||
c1bb20dec0 | |||
12b63a506f | |||
a195e4b55b | |||
8ad9e73abb | |||
|
a518158ccf | ||
|
ad2f6f3a7f | ||
|
e2d8f8a096 | ||
|
11b554daad | ||
|
17ebbb71e8 | ||
|
dde11f73f5 | ||
|
790ece14d1 | ||
|
45dd74aad4 | ||
|
b3539e6c7e | ||
|
f3ecc12494 | ||
|
fe4bc3acef | ||
a32e8e9d05 | |||
013bb632d3 | |||
691180b3f0 | |||
dc74a064ba | |||
820ff2be00 | |||
96f799f535 | |||
0f91ce4940 | |||
95bb55aad8 | |||
0ac35c3d43 | |||
a5238bfbdc | |||
25dab73a83 | |||
72648ea14d | |||
4f19da5987 | |||
fd110f5c6f | |||
8fb888a395 | |||
10931bd156 | |||
a6f01d0864 | |||
|
b719efb58c | ||
|
bfd17b0686 | ||
|
a87560ff96 | ||
|
c96d264c8f | ||
|
0b4e3a9341 | ||
|
6aa30f537b | ||
|
ffcab790fc | ||
|
b065d15f60 | ||
|
319fa32754 | ||
|
15a3b11d2e | ||
|
ec99a5ff75 | ||
|
2a19f14adb | ||
|
1667b8c1df | ||
|
5b4aa5c73e | ||
|
3cae12b256 | ||
|
39db6303c2 | ||
|
8955df84cd | ||
|
8d920e1ba1 | ||
|
71b252843c | ||
|
1deda523b4 | ||
|
73d92f25c1 | ||
|
798608cebd | ||
|
4b85e01e93 | ||
|
c1a64e2094 | ||
|
8fd79ce23e | ||
|
70571cb6ac | ||
|
f5d535daa7 | ||
|
d358620b46 | ||
|
e4c1305da5 | ||
|
53c665787e | ||
|
2722c92bcf | ||
|
a1fd36aa6f | ||
|
ae3f97d890 | ||
|
17f07dafc0 | ||
|
20c91e8e31 | ||
|
a52c20993b | ||
|
dd76fda274 | ||
|
2bac177618 | ||
|
06b1eca306 | ||
|
360182b6bb | ||
|
7feec266b8 | ||
|
de3f1a534b | ||
|
97f5c84f23 | ||
|
7f6df8d032 | ||
|
39ded93a42 | ||
|
4f7a27dd8d | ||
|
cc906e9b22 | ||
|
bb44c1a216 | ||
|
3ff7bf1571 | ||
|
5297ae8a62 | ||
|
fb416306e7 | ||
|
464407e7ee | ||
|
3723d644e6 | ||
|
f6f502618d | ||
|
b0a317d709 | ||
|
8ba6a93214 | ||
|
768744ad27 | ||
|
606f357839 | ||
|
8777024cd8 | ||
|
642446e592 | ||
|
d1fbb4b42b | ||
|
5e82f091c0 | ||
|
28438c6423 | ||
|
49a4830d89 | ||
|
9e889008bf | ||
|
2590997b1a | ||
|
c1080481ec | ||
|
c8162b8eb9 | ||
|
849fd8d56b | ||
|
cc94426902 | ||
|
360c1e46a1 | ||
|
b8f85960aa | ||
|
b9ec64fd4f | ||
|
69e5a5f12c | ||
|
2d7437fc0d | ||
|
03f37f33ff | ||
|
16cc0c9f8d | ||
|
e1a4db700f | ||
|
a16277d9bb | ||
|
8ed9e38290 | ||
|
d53643ef55 | ||
|
796c72dd09 | ||
|
40d12b906b | ||
|
3fb6c0a052 | ||
|
16b7e0b493 | ||
|
621a132e35 | ||
|
ebf08ea414 | ||
|
9659621ba0 | ||
|
edc06e51fb | ||
|
a4ed29b560 | ||
|
7775105da6 | ||
|
d7b5d9a272 | ||
|
62f587d7e6 | ||
|
6dab5bded8 | ||
|
e9ada2468e | ||
|
db0ded44e0 | ||
|
d1a7e399dc | ||
|
b9b4416737 | ||
|
1e9f15b40f | ||
|
8f5293662e | ||
|
d0a56de07e | ||
|
bdcfe07fb0 | ||
|
0aa7ff3b2c | ||
|
b7b6a575bc | ||
|
8d194c5523 | ||
|
05c5d35dbf | ||
|
99a62828f5 | ||
|
b4e569ccc8 | ||
|
48a76bacb6 | ||
|
8a8e299c7b | ||
|
7908676063 | ||
|
6fa88daeb0 | ||
|
17e7b47cdc | ||
|
cc49692545 | ||
|
1a92d89911 | ||
|
7c5369203c | ||
|
c0ad46ba65 | ||
|
5be4fe6784 | ||
|
7acb9e964b | ||
|
4243da9c32 | ||
|
f7439172b6 | ||
|
b2e45fcaee | ||
|
eae1b16797 | ||
|
4cbe990ec1 | ||
|
7277d7407a | ||
|
ccd6a11c8a | ||
|
00f215b780 | ||
|
10e6c73b2b | ||
|
7a963c8461 | ||
|
d463239b17 | ||
|
fa62a47680 | ||
|
f22813bb5b | ||
|
8b08f79877 | ||
|
9515e67493 | ||
|
53fc25612b | ||
|
71d2faedb1 |
34
.env.example
34
.env.example
@ -1,32 +1,34 @@
|
|||||||
|
APP_DEBUG=false
|
||||||
APP_NAME=OSB
|
APP_NAME=OSB
|
||||||
APP_NAME_HTML_LONG="<b>Graytech</b>Hosting"
|
APP_NAME_HTML_LONG="<b>Graytech</b>Hosting"
|
||||||
APP_NAME_HTML_SHORT="<b>G</b>H"
|
APP_NAME_HTML_SHORT="<b>G</b>H"
|
||||||
APP_ENV=production
|
APP_ENV=production
|
||||||
APP_KEY=
|
APP_KEY=
|
||||||
APP_DEBUG=false
|
APP_TIMEZONE=Australia/Melbourne
|
||||||
APP_URL=https://www.graytech.net.au
|
APP_URL=https://www.graytech.net.au
|
||||||
|
|
||||||
LOG_CHANNEL=stack
|
LOG_CHANNEL=daily
|
||||||
|
|
||||||
DB_CONNECTION=mysql
|
DB_CONNECTION=pgsql
|
||||||
DB_HOST=database
|
DB_HOST=postgres
|
||||||
DB_PORT=3306
|
DB_PORT=5432
|
||||||
DB_DATABASE=database
|
DB_DATABASE=graytech
|
||||||
DB_USERNAME=homestead
|
DB_USERNAME=graytech
|
||||||
DB_PASSWORD=secret
|
DB_PASSWORD=
|
||||||
|
DB_SCHEMA=billing
|
||||||
|
|
||||||
BROADCAST_DRIVER=log
|
BROADCAST_DRIVER=log
|
||||||
CACHE_DRIVER=file
|
CACHE_STORE=file
|
||||||
SESSION_DRIVER=file
|
SESSION_DRIVER=file
|
||||||
SESSION_LIFETIME=120
|
SESSION_LIFETIME=120
|
||||||
QUEUE_DRIVER=database
|
QUEUE_CONNECTION=database
|
||||||
|
|
||||||
REDIS_HOST=127.0.0.1
|
REDIS_HOST=127.0.0.1
|
||||||
REDIS_PASSWORD=null
|
REDIS_PASSWORD=null
|
||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
|
|
||||||
MAIL_DRIVER=smtp
|
MAIL_MAILER=smtp
|
||||||
MAIL_HOST=MAIL
|
MAIL_HOST=smtp
|
||||||
MAIL_PORT=25
|
MAIL_PORT=25
|
||||||
MAIL_USERNAME=null
|
MAIL_USERNAME=null
|
||||||
MAIL_PASSWORD=null
|
MAIL_PASSWORD=null
|
||||||
@ -43,13 +45,13 @@ MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
|||||||
EZYPAY_TOKEN=
|
EZYPAY_TOKEN=
|
||||||
EZYPAY_GUID=
|
EZYPAY_GUID=
|
||||||
|
|
||||||
QUICKBOOKS_CLIENT_ID=
|
|
||||||
QUICKBOOKS_CLIENT_SECRET=
|
|
||||||
QUICKBOOKS_API_URL=Production
|
|
||||||
|
|
||||||
AUTH_GOOGLE_CLIENT_ID=
|
AUTH_GOOGLE_CLIENT_ID=
|
||||||
AUTH_GOOGLE_SECRET=
|
AUTH_GOOGLE_SECRET=
|
||||||
|
|
||||||
|
AUTH_INTUIT_CLIENT_ID=
|
||||||
|
AUTH_INTUIT_SECRET_KEY=
|
||||||
|
INTUIT_VERIFYTOKEN=
|
||||||
|
|
||||||
PAYPAL_MODE=sandbox
|
PAYPAL_MODE=sandbox
|
||||||
PAYPAL_SANDBOX_CLIENT_ID=
|
PAYPAL_SANDBOX_CLIENT_ID=
|
||||||
PAYPAL_SANDBOX_SECRET=
|
PAYPAL_SANDBOX_SECRET=
|
||||||
|
147
.gitea/workflows/build_docker.yaml
Normal file
147
.gitea/workflows/build_docker.yaml
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
name: Create Docker Image
|
||||||
|
run-name: ${{ gitea.actor }} Building Docker Image 🐳
|
||||||
|
on: [push]
|
||||||
|
env:
|
||||||
|
VERSION: latest
|
||||||
|
DOCKER_HOST: tcp://127.0.0.1:2375
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# test:
|
||||||
|
# strategy:
|
||||||
|
# matrix:
|
||||||
|
# arch:
|
||||||
|
# - x86_64
|
||||||
|
# # arm64
|
||||||
|
#
|
||||||
|
# name: Test Application
|
||||||
|
# runs-on: docker-${{ matrix.arch }}
|
||||||
|
# container:
|
||||||
|
# image: gitea.dege.au/docker/php:8.3-fpm-pgsql-server-test
|
||||||
|
#
|
||||||
|
# steps:
|
||||||
|
# - name: Environment Setup
|
||||||
|
# run: |
|
||||||
|
# # If we have a proxy use it
|
||||||
|
# if [ -n "${HTTP_PROXY}" ]; then echo "HTTP PROXY [${HTTP_PROXY}]"; sed -i -e s'/https/http/' /etc/apk/repositories; fi
|
||||||
|
# # Some pre-reqs
|
||||||
|
# apk add git nodejs
|
||||||
|
# ## Some debugging info
|
||||||
|
# # env|sort
|
||||||
|
#
|
||||||
|
# - name: Code Checkout
|
||||||
|
# uses: actions/checkout@v4
|
||||||
|
#
|
||||||
|
# - name: Run Tests
|
||||||
|
# run: |
|
||||||
|
# mv .env.testing .env
|
||||||
|
# # Install Composer and project dependencies.
|
||||||
|
# mkdir -p ${COMPOSER_HOME}
|
||||||
|
# if [ -n "${{ secrets.COMPOSER_GITHUB_TOKEN }}" ]; then echo ${{ secrets.COMPOSER_GITHUB_TOKEN }} > ${COMPOSER_HOME}/auth.json; fi
|
||||||
|
# composer install
|
||||||
|
# # Generate an application key. Re-cache.
|
||||||
|
# php artisan key:generate
|
||||||
|
# php artisan migrate
|
||||||
|
# php artisan db:seed
|
||||||
|
# # run laravel tests
|
||||||
|
# touch storage/app/test/*ZIP storage/app/test/file/*
|
||||||
|
# XDEBUG_MODE=coverage php vendor/bin/phpunit --coverage-text --colors=never
|
||||||
|
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
arch:
|
||||||
|
- x86_64
|
||||||
|
# - arm64
|
||||||
|
# needs: [test]
|
||||||
|
|
||||||
|
name: Build Docker Image
|
||||||
|
runs-on: docker-${{ matrix.arch }}
|
||||||
|
container:
|
||||||
|
image: docker:dind
|
||||||
|
privileged: true
|
||||||
|
env:
|
||||||
|
ARCH: ${{ matrix.arch }}
|
||||||
|
VERSIONARCH: ${{ env.VERSION }}-${{ env.ARCH }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Environment Setup
|
||||||
|
run: |
|
||||||
|
# If we have a proxy use it
|
||||||
|
if [ -n "${HTTP_PROXY}" ]; then echo "HTTP PROXY [${HTTP_PROXY}]"; sed -i -e s'/https/http/' /etc/apk/repositories; fi
|
||||||
|
# Some pre-reqs
|
||||||
|
apk add git curl nodejs
|
||||||
|
# Start docker
|
||||||
|
( dockerd --host=tcp://0.0.0.0:2375 --tls=false & ) && sleep 3
|
||||||
|
## Some debugging info
|
||||||
|
# docker info && docker version
|
||||||
|
env|sort
|
||||||
|
echo "PRT: ${{ secrets.PKG_WRITE_TOKEN }}"
|
||||||
|
|
||||||
|
- name: Registry FQDN Setup
|
||||||
|
id: registry
|
||||||
|
run: |
|
||||||
|
registry=${{ github.server_url }}
|
||||||
|
echo "registry=${registry##http*://}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Container Registry Login
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ${{ steps.registry.outputs.registry }}
|
||||||
|
username: ${{ gitea.actor }}
|
||||||
|
password: ${{ secrets.PKG_WRITE_TOKEN }}
|
||||||
|
|
||||||
|
- name: Code Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Record version
|
||||||
|
run: |
|
||||||
|
pwd
|
||||||
|
ls -al
|
||||||
|
echo ${GITHUB_SHA::8} > VERSION
|
||||||
|
cat VERSION
|
||||||
|
|
||||||
|
- name: Build and Push Docker Image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: docker/Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: "${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.VERSIONARCH }}"
|
||||||
|
|
||||||
|
manifest:
|
||||||
|
name: Final Docker Image Manifest
|
||||||
|
runs-on: docker-x86_64
|
||||||
|
container:
|
||||||
|
image: docker:dind
|
||||||
|
privileged: true
|
||||||
|
needs: [build]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Environment Setup
|
||||||
|
run: |
|
||||||
|
# If we have a proxy use it
|
||||||
|
if [ -n "${HTTP_PROXY}" ]; then echo "HTTP PROXY [${HTTP_PROXY}]"; sed -i -e s'/https/http/' /etc/apk/repositories; fi
|
||||||
|
# Some pre-reqs
|
||||||
|
apk add git curl nodejs
|
||||||
|
# Start docker
|
||||||
|
( dockerd --host=tcp://0.0.0.0:2375 --tls=false & ) && sleep 3
|
||||||
|
|
||||||
|
- name: Registry FQDN Setup
|
||||||
|
id: registry
|
||||||
|
run: |
|
||||||
|
registry=${{ github.server_url }}
|
||||||
|
echo "registry=${registry##http*://}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Container Registry Login
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ${{ steps.registry.outputs.registry }}
|
||||||
|
username: ${{ gitea.actor }}
|
||||||
|
password: ${{ secrets.PKG_WRITE_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build Docker Manifest
|
||||||
|
run: |
|
||||||
|
docker manifest create ${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.VERSION }} \
|
||||||
|
${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.VERSION }}-x86_64
|
||||||
|
# ${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.VERSION }}-arm64
|
||||||
|
docker manifest push --purge ${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.VERSION }}
|
@ -1,14 +0,0 @@
|
|||||||
stages:
|
|
||||||
- test
|
|
||||||
- build
|
|
||||||
|
|
||||||
# This folder is cached between builds
|
|
||||||
# http://docs.gitlab.com/ce/ci/yaml/README.html#cache
|
|
||||||
cache:
|
|
||||||
key: ${CI_COMMIT_REF_SLUG}
|
|
||||||
paths:
|
|
||||||
- vendor/
|
|
||||||
|
|
||||||
include:
|
|
||||||
- .gitlab-test.yml
|
|
||||||
- .gitlab-docker-x86_64.yml
|
|
@ -1,33 +0,0 @@
|
|||||||
docker:
|
|
||||||
image: docker:latest
|
|
||||||
|
|
||||||
stage: build
|
|
||||||
|
|
||||||
services:
|
|
||||||
- docker:dind
|
|
||||||
|
|
||||||
variables:
|
|
||||||
VERSION: latest
|
|
||||||
CACHETAG: build-${VERSION}
|
|
||||||
DOCKER_HOST: tcp://docker:2375
|
|
||||||
|
|
||||||
tags:
|
|
||||||
- docker
|
|
||||||
- x86_64
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- docker info
|
|
||||||
- docker version
|
|
||||||
- echo "$CI_JOB_TOKEN" | docker login -u "$CI_REGISTRY_USER" "$CI_REGISTRY" --password-stdin
|
|
||||||
- if [ -n "$GITHUB_TOKEN" ]; then cat $GITHUB_TOKEN |base64 -d > auth.json; fi
|
|
||||||
|
|
||||||
script:
|
|
||||||
- if [ -f init ]; then chmod 500 init; fi
|
|
||||||
- ([ -z "$REFRESH" ] && docker pull ${CI_REGISTRY_IMAGE}:${CACHETAG}) || echo "true"
|
|
||||||
- echo -n ${CI_COMMIT_SHORT_SHA} > VERSION
|
|
||||||
- rm -rf vendor/
|
|
||||||
- docker build --cache-from ${CI_REGISTRY_IMAGE}:${CACHETAG} -t ${CI_REGISTRY_IMAGE}:${VERSION} -t ${CI_REGISTRY_IMAGE}:${CACHETAG} .
|
|
||||||
- docker push ${CI_REGISTRY_IMAGE}:${VERSION}
|
|
||||||
- docker push ${CI_REGISTRY_IMAGE}:${CACHETAG}
|
|
@ -1,48 +0,0 @@
|
|||||||
test:
|
|
||||||
image: registry.leenooks.net/leenooks/php:8.0-fpm-ext-test
|
|
||||||
|
|
||||||
stage: test
|
|
||||||
|
|
||||||
# NOTE: This service is dependant on project file configuration, which is not there if the cache was deleted
|
|
||||||
# resulting in the testing to fail on the first run.
|
|
||||||
services:
|
|
||||||
- mariadb:10.5
|
|
||||||
|
|
||||||
variables:
|
|
||||||
MYSQL_DATABASE: testing
|
|
||||||
MYSQL_ROOT_PASSWORD: test
|
|
||||||
MYSQL_USER: test
|
|
||||||
MYSQL_PASSWORD: test
|
|
||||||
|
|
||||||
tags:
|
|
||||||
- php
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
- test
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- mv .env.testing .env
|
|
||||||
|
|
||||||
# Install Composer and project dependencies.
|
|
||||||
- mkdir -p /root/.config/composer
|
|
||||||
- if [ -n "$GITHUB_TOKEN" ]; then cat $GITHUB_TOKEN |base64 -d > /root/.config/composer/auth.json ; fi
|
|
||||||
- composer install
|
|
||||||
|
|
||||||
# Add mysql client for schema pre-load
|
|
||||||
- apt update && apt install -f mariadb-client
|
|
||||||
|
|
||||||
# Generate an application key. Re-cache.
|
|
||||||
- php artisan key:generate --env=testing
|
|
||||||
- php artisan config:cache --env=testing
|
|
||||||
- php artisan migrate
|
|
||||||
- php artisan db:seed
|
|
||||||
|
|
||||||
script:
|
|
||||||
# run laravel tests
|
|
||||||
- XDEBUG_MODE=coverage php vendor/bin/phpunit --coverage-text --colors=never
|
|
||||||
|
|
||||||
# run frontend tests
|
|
||||||
# if you have any task for testing frontend
|
|
||||||
# set it in your package.json script
|
|
||||||
# comment this out if you don't have a frontend test
|
|
||||||
# npm test
|
|
13
Dockerfile
13
Dockerfile
@ -1,13 +0,0 @@
|
|||||||
FROM registry.leenooks.net/leenooks/php:8.0-fpm-image
|
|
||||||
|
|
||||||
COPY . /var/www/html/
|
|
||||||
|
|
||||||
RUN export COMPOSER_HOME=/var/www/.composer \
|
|
||||||
&& mkdir -p /var/www/.composer \
|
|
||||||
&& ([ -r auth.json ] && mv auth.json /var/www/.composer/) || true \
|
|
||||||
&& touch .composer.refresh \
|
|
||||||
&& mv .env.example .env \
|
|
||||||
&& FORCE_PERMS=1 NGINX_START=FALSE /sbin/init \
|
|
||||||
&& chmod +x /var/www/html/artisan \
|
|
||||||
&& /var/www/html/artisan storage:link \
|
|
||||||
&& rm -rf /var/www/.composer
|
|
7
app/Classes/External/Accounting.php
vendored
7
app/Classes/External/Accounting.php
vendored
@ -1,7 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Classes\External;
|
|
||||||
|
|
||||||
abstract class Accounting
|
|
||||||
{
|
|
||||||
}
|
|
68
app/Classes/External/Accounting/Quickbooks.php
vendored
68
app/Classes/External/Accounting/Quickbooks.php
vendored
@ -1,68 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Classes\External\Accounting;
|
|
||||||
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
use Illuminate\Support\Facades\Cache;
|
|
||||||
use QuickBooksOnline\API\Data\IPPCustomer;
|
|
||||||
|
|
||||||
use App\Classes\External\Accounting as Base;
|
|
||||||
use App\Models\User;
|
|
||||||
|
|
||||||
class Quickbooks extends Base
|
|
||||||
{
|
|
||||||
private $api = NULL;
|
|
||||||
|
|
||||||
public function __construct(User $uo)
|
|
||||||
{
|
|
||||||
if (Auth::user())
|
|
||||||
throw new \Exception('User logged in - *TODO* handle this');
|
|
||||||
|
|
||||||
Auth::loginUsingId($uo->id);
|
|
||||||
|
|
||||||
$this->api = app('Spinen\QuickBooks\Client');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCustomers($refresh=FALSE): Collection
|
|
||||||
{
|
|
||||||
if ($refresh)
|
|
||||||
Cache::forget(__METHOD__);
|
|
||||||
|
|
||||||
return Cache::remember(__METHOD__,86400,function() {
|
|
||||||
return collect($this->api->getDataService()->Query('SELECT * FROM Customer'));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getInvoice(int $id,$refresh=FALSE)
|
|
||||||
{
|
|
||||||
if ($refresh)
|
|
||||||
Cache::forget(__METHOD__.$id);
|
|
||||||
|
|
||||||
return Cache::remember(__METHOD__.$id,86400,function() use ($id) {
|
|
||||||
return $this->api->getDataService()->Query(sprintf("SELECT * FROM Invoice where id = '%s'",$id));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getInvoices($refresh=FALSE): Collection
|
|
||||||
{
|
|
||||||
if ($refresh)
|
|
||||||
Cache::forget(__METHOD__);
|
|
||||||
|
|
||||||
return Cache::remember(__METHOD__,86400,function() {
|
|
||||||
return collect($this->api->getDataService()->Query('SELECT * FROM Invoice'));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updateCustomer(IPPCustomer $r,array $args)
|
|
||||||
{
|
|
||||||
$r->sparse = TRUE;
|
|
||||||
|
|
||||||
foreach ($args as $k=>$v)
|
|
||||||
{
|
|
||||||
$r->{$k} = $v;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->api->getDataService()->Update($r);
|
|
||||||
}
|
|
||||||
}
|
|
77
app/Classes/External/Payments/Ezypay.php
vendored
77
app/Classes/External/Payments/Ezypay.php
vendored
@ -30,8 +30,8 @@ class Ezypay extends Payments
|
|||||||
$api_remain = Arr::get($result->getHeader('X-RateLimit-Remaining'),0);
|
$api_remain = Arr::get($result->getHeader('X-RateLimit-Remaining'),0);
|
||||||
$api_reset = Arr::get($result->getHeader('X-RateLimit-Reset'),0);
|
$api_reset = Arr::get($result->getHeader('X-RateLimit-Reset'),0);
|
||||||
|
|
||||||
if ($api_remain == 0) {
|
if ($api_remain === 0) {
|
||||||
Log::error('API Throttle.',['m'=>__METHOD__]);
|
Log::error('API Throttle.',['m'=>__METHOD__,'api_reset'=>$api_reset]);
|
||||||
Cache::put('api_throttle',$api_reset,now()->addSeconds($api_reset));
|
Cache::put('api_throttle',$api_reset,now()->addSeconds($api_reset));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,6 +45,56 @@ class Ezypay extends Payments
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of configured customers
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
* @todo Hard coded at 100 clients - need to make this more dynamic
|
||||||
|
*
|
||||||
|
* {#1079
|
||||||
|
* +"TermsAndConditions": true
|
||||||
|
* +"Account": {#1082
|
||||||
|
* +"PaymentMethodId": 0
|
||||||
|
* }
|
||||||
|
* +"Address1": "1 Road Street "
|
||||||
|
* +"BillingStatus": "Active"
|
||||||
|
* +"BusinessAccountReference": "12345"
|
||||||
|
* +"CountryCode": "AU"
|
||||||
|
* +"Email": "user@example.com"
|
||||||
|
* +"EzypayReferenceNumber": 12345678
|
||||||
|
* +"Firstname": "Bart"
|
||||||
|
* +"Id": "6219c42b-8e56-4e4e-af3a-76ddf4b0e4e1"
|
||||||
|
* +"MobilePhone": ""
|
||||||
|
* +"Postcode": "1234"
|
||||||
|
* +"ReferenceId": "01nnnn"
|
||||||
|
* +"State": "VIC"
|
||||||
|
* +"Suburb": "TOWN"
|
||||||
|
* +"Towncity": ""
|
||||||
|
* +"Surname": "Simpson "
|
||||||
|
* +"PaymentPlanId": "00000000-0000-0000-0000-000000000000"
|
||||||
|
* +"DateOfBirth": "1970-01-01T00:00:00.000"
|
||||||
|
* +"Gender": "M"
|
||||||
|
* +"DebitType": 0
|
||||||
|
* +"RecurringAmount": 0.0
|
||||||
|
* +"Frequency": 0
|
||||||
|
* +"FrequencyType": 0
|
||||||
|
* +"StartDate": "0001-01-01T00:00:00.000"
|
||||||
|
* +"RecurringDebitEndType": 0
|
||||||
|
* +"TotalAmountCollected": 0.0
|
||||||
|
* +"MinimumNumberOfPayment": 0
|
||||||
|
* +"RecurringWithDifferentFirstDebitAmount": 0.0
|
||||||
|
* +"RecurringDebitFirstDebitDate": "0001-01-01T00:00:00.000"
|
||||||
|
* +"RecurringDebitAmount": 0.0
|
||||||
|
* +"RecurringDebitFrequency": 0
|
||||||
|
* +"RecurringDebitFrequencyType": 0
|
||||||
|
* +"RecurringDebitStartDate": "0001-01-01T00:00:00.000"
|
||||||
|
* +"RecurringDebitDifferentFirstAmountEndType": 0
|
||||||
|
* +"RecurringDebitTotalAmountCollected": 0.0
|
||||||
|
* +"RecurringDebitMinimumNumberOfPayment": 0
|
||||||
|
* +"OnceOffAmount": 0.0
|
||||||
|
* +"OnceOffStartDate": "0001-01-01T00:00:00.000"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
public function getCustomers(): Collection
|
public function getCustomers(): Collection
|
||||||
{
|
{
|
||||||
return Cache::remember(__METHOD__,86400,function() {
|
return Cache::remember(__METHOD__,86400,function() {
|
||||||
@ -52,6 +102,29 @@ class Ezypay extends Payments
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Specific debits for a client.
|
||||||
|
*
|
||||||
|
* @param array $opt
|
||||||
|
* @return Collection
|
||||||
|
*
|
||||||
|
* Illuminate\Support\Collection^ {#1077
|
||||||
|
* #items: array:4 [
|
||||||
|
* 0 => {#1826
|
||||||
|
* +"Amount": 99.99
|
||||||
|
* +"Id": "76666ef1-106c-458c-9162-e77fe746517c"
|
||||||
|
* +"CustomerId": "6219c42b-8e56-4e4e-af3a-76ddf4b0e4e1"
|
||||||
|
* +"Date": "2021-10-01T00:00:00.000"
|
||||||
|
* +"Status": "Pending"
|
||||||
|
* }
|
||||||
|
* 1 => {#2075
|
||||||
|
* +"Amount": 99.99
|
||||||
|
* +"Id": "80ba201d-fb6f-4700-b1b7-2b13fbfa91d5"
|
||||||
|
* +"CustomerId": "6219c42b-8e56-4e4e-af3a-76ddf4b0e4e1"
|
||||||
|
* +"Date": "2021-09-01T00:00:00.000"
|
||||||
|
* +"Status": "Pending"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
public function getDebits($opt=[]): Collection
|
public function getDebits($opt=[]): Collection
|
||||||
{
|
{
|
||||||
return Cache::remember(__METHOD__.http_build_query($opt),86400,function() use ($opt) {
|
return Cache::remember(__METHOD__.http_build_query($opt),86400,function() use ($opt) {
|
||||||
|
26
app/Classes/External/Supplier.php
vendored
26
app/Classes/External/Supplier.php
vendored
@ -17,6 +17,8 @@ abstract class Supplier
|
|||||||
protected $o = NULL;
|
protected $o = NULL;
|
||||||
protected $_columns = [];
|
protected $_columns = [];
|
||||||
|
|
||||||
|
public const traffic_connection_keys = ['user','pass','url'];
|
||||||
|
|
||||||
public function __construct(Model $o)
|
public function __construct(Model $o)
|
||||||
{
|
{
|
||||||
$this->o = $o;
|
$this->o = $o;
|
||||||
@ -26,24 +28,29 @@ abstract class Supplier
|
|||||||
/**
|
/**
|
||||||
* Connect and pull down traffic data
|
* Connect and pull down traffic data
|
||||||
*
|
*
|
||||||
|
* @param array $connection
|
||||||
|
* @param string $type
|
||||||
* @return Collection
|
* @return Collection
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function fetch(): Collection
|
public function fetch(array $connection,string $type): Collection
|
||||||
{
|
{
|
||||||
|
if (count(array_intersect(array_keys($connection),self::traffic_connection_keys)) !== 3)
|
||||||
|
throw new \Exception('No or missing connection details for:'.$type);
|
||||||
|
|
||||||
if ($x=$this->mustPause()) {
|
if ($x=$this->mustPause()) {
|
||||||
Log::notice(sprintf('%s:API Throttle, waiting [%s]...',self::LOGKEY,$x),['m'=>__METHOD__]);
|
Log::notice(sprintf('%s:API Throttle, waiting [%s]...',self::LOGKEY,$x),['m'=>__METHOD__]);
|
||||||
sleep($x);
|
sleep($x);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::debug(sprintf('%s:Supplier [%d], fetch data for [%s]...',self::LOGKEY,$this->o->id,$this->o->stats_lastupdate),['m'=>__METHOD__]);
|
Log::debug(sprintf('%s:Supplier [%d], fetch data for [%s]...',self::LOGKEY,$this->o->id,Arr::get($connection,'last')),['m'=>__METHOD__]);
|
||||||
$key = 'Supplier:'.$this->o->id.$this->o->stats_lastupdate;
|
$key = 'Supplier:'.$this->o->id.Arr::get($connection,'last');
|
||||||
$result = Cache::remember($key,86400,function() {
|
|
||||||
$client = $this->getClient();
|
|
||||||
|
|
||||||
$response = Http::get($this->o->stats_url,[
|
$result = Cache::remember($key,86400,function() use ($connection) {
|
||||||
$this->login_user_field => $this->o->stats_username,
|
$response = Http::get(Arr::get($connection,'url'),[
|
||||||
$this->login_pass_field => $this->o->stats_password,
|
$this->login_user_field => Arr::get($connection,'user'),
|
||||||
$this->date_field => $this->o->stats_lastupdate->format('Y-m-d'),
|
$this->login_pass_field => Arr::get($connection,'pass'),
|
||||||
|
$this->date_field => Arr::get($connection,'last'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// @todo These API rate limiting is untested.
|
// @todo These API rate limiting is untested.
|
||||||
@ -55,7 +62,6 @@ abstract class Supplier
|
|||||||
Cache::put('api_throttle',$api_reset,now()->addSeconds($api_reset));
|
Cache::put('api_throttle',$api_reset,now()->addSeconds($api_reset));
|
||||||
}
|
}
|
||||||
|
|
||||||
//dd($response->header('Content-Type'),$response->headers());
|
|
||||||
// Assume the supplier provides an ASCII output for text/html
|
// Assume the supplier provides an ASCII output for text/html
|
||||||
if (preg_match('#^text/html;#',$x=$response->header('Content-Type'))) {
|
if (preg_match('#^text/html;#',$x=$response->header('Content-Type'))) {
|
||||||
return collect(explode("\n",$response->body()))->filter();
|
return collect(explode("\n",$response->body()))->filter();
|
||||||
|
62
app/Console/Commands/AccountingAccountAdd.php
Normal file
62
app/Console/Commands/AccountingAccountAdd.php
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
use Intuit\Jobs\AccountingCustomerUpdate;
|
||||||
|
use Intuit\Models\Customer as AccAccount;
|
||||||
|
|
||||||
|
use App\Models\{Account,ProviderOauth,Site,User};
|
||||||
|
|
||||||
|
class AccountingAccountAdd extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'accounting:account:add'
|
||||||
|
.' {siteid : Site ID}'
|
||||||
|
.' {provider : Provider Name}'
|
||||||
|
.' {user : User Email}'
|
||||||
|
.' {id : Account ID}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Add an account to the accounting provider';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$site = Site::findOrFail($this->argument('siteid'));
|
||||||
|
Config::set('site',$site);
|
||||||
|
|
||||||
|
$uo = User::where('email',$this->argument('user'))->singleOrFail();
|
||||||
|
|
||||||
|
$so = ProviderOauth::where('name',$this->argument('provider'))->singleOrFail();
|
||||||
|
if (! ($to=$so->token($uo)))
|
||||||
|
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
||||||
|
|
||||||
|
$o = Account::findOrFail($this->argument('id'));
|
||||||
|
|
||||||
|
$acc = new AccAccount;
|
||||||
|
$acc->PrimaryEmailAddr = (object)['Address'=>$o->user->email];
|
||||||
|
$acc->ResaleNum = $o->sid;
|
||||||
|
$acc->GivenName = $o->user->firstname;
|
||||||
|
$acc->FamilyName = $o->user->lastname;
|
||||||
|
$acc->CompanyName = $o->name;
|
||||||
|
$acc->DisplayName = $o->name;
|
||||||
|
$acc->FullyQualifiedName = $o->name;
|
||||||
|
$acc->Active = (bool)$o->active;
|
||||||
|
|
||||||
|
return AccountingCustomerUpdate::dispatchSync($to,$acc);
|
||||||
|
}
|
||||||
|
}
|
58
app/Console/Commands/AccountingAccountGet.php
Normal file
58
app/Console/Commands/AccountingAccountGet.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use GuzzleHttp\Exception\ConnectException;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
use Intuit\Exceptions\ConnectionIssueException;
|
||||||
|
|
||||||
|
use App\Models\{ProviderOauth,Site,User};
|
||||||
|
|
||||||
|
class AccountingAccountGet extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'accounting:account:get'
|
||||||
|
.' {siteid : Site ID}'
|
||||||
|
.' {provider : Provider Name}'
|
||||||
|
.' {user : User Email}'
|
||||||
|
.' {id : Account ID}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Get an account from the accounting provider';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$site = Site::findOrFail($this->argument('siteid'));
|
||||||
|
Config::set('site',$site);
|
||||||
|
|
||||||
|
$uo = User::where('email',$this->argument('user'))->singleOrFail();
|
||||||
|
|
||||||
|
$so = ProviderOauth::where('name',$this->argument('provider'))->singleOrFail();
|
||||||
|
if (! ($to=$so->token($uo)))
|
||||||
|
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = $to->API();
|
||||||
|
dump($api->getAccountQuery($this->argument('id')));
|
||||||
|
|
||||||
|
} catch (ConnectException|ConnectionIssueException $e) {
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
app/Console/Commands/AccountingAccountSync.php
Normal file
51
app/Console/Commands/AccountingAccountSync.php
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
|
use App\Models\{ProviderOauth,Site,User};
|
||||||
|
use App\Jobs\AccountingAccountSync as Job;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronise Customers with Accounts
|
||||||
|
*/
|
||||||
|
class AccountingAccountSync extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'accounting:account:sync'
|
||||||
|
.' {siteid : Site ID}'
|
||||||
|
.' {provider : Provider Name}'
|
||||||
|
.' {user : User Email}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Synchronise accounts with accounting system';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$site = Site::findOrFail($this->argument('siteid'));
|
||||||
|
Config::set('site',$site);
|
||||||
|
|
||||||
|
$uo = User::where('email',$this->argument('user'))->singleOrFail();
|
||||||
|
|
||||||
|
$so = ProviderOauth::where('name',$this->argument('provider'))->singleOrFail();
|
||||||
|
if (! ($to=$so->token($uo)))
|
||||||
|
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
||||||
|
|
||||||
|
Job::dispatchSync($to);
|
||||||
|
}
|
||||||
|
}
|
110
app/Console/Commands/AccountingInvoiceAdd.php
Normal file
110
app/Console/Commands/AccountingInvoiceAdd.php
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
use Intuit\Jobs\AccountingInvoiceUpdate;
|
||||||
|
use Intuit\Models\Invoice as AccInvoice;
|
||||||
|
|
||||||
|
use App\Models\{Invoice,ProviderOauth,Site,User};
|
||||||
|
|
||||||
|
class AccountingInvoiceAdd extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'accounting:invoice:add'
|
||||||
|
.' {siteid : Site ID}'
|
||||||
|
.' {provider : Provider Name}'
|
||||||
|
.' {user : User Email}'
|
||||||
|
.' {id : Invoice ID}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Add an invoice to the accounting provider';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$site = Site::findOrFail($this->argument('siteid'));
|
||||||
|
Config::set('site',$site);
|
||||||
|
|
||||||
|
$uo = User::where('email',$this->argument('user'))->singleOrFail();
|
||||||
|
|
||||||
|
$so = ProviderOauth::where('name',$this->argument('provider'))->singleOrFail();
|
||||||
|
if (! ($to=$so->token($uo)))
|
||||||
|
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
||||||
|
|
||||||
|
$o = Invoice::findOrFail($this->argument('id'));
|
||||||
|
|
||||||
|
// Check the customer exists
|
||||||
|
if ($o->account->providers->where('pivot.provider_oauth_id',$so->id)->count() !== 1)
|
||||||
|
throw new \Exception(sprintf('Account [%d] for Invoice [%d] not defined',$o->account_id,$o->id));
|
||||||
|
|
||||||
|
$ao = $o->account->providers->where('pivot.provider_oauth_id',$so->id)->pop();
|
||||||
|
|
||||||
|
// Some validation
|
||||||
|
if (! $ao->pivot->ref) {
|
||||||
|
$this->error(sprintf('Accounting not defined for account [%d]',$o->account_id));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$acc = new AccInvoice;
|
||||||
|
$acc->CustomerRef = (object)['value'=>$ao->pivot->ref];
|
||||||
|
$acc->DocNumber = $o->lid;
|
||||||
|
$acc->TxnDate = $o->created_at->format('Y-m-d');
|
||||||
|
$acc->DueDate = $o->due_at->format('Y-m-d');
|
||||||
|
|
||||||
|
$lines = collect();
|
||||||
|
$c = 0;
|
||||||
|
|
||||||
|
// @todo Group these by ItemRef and the Same Unit Price and Description, so that we can then use quantity to represent the number of them.
|
||||||
|
foreach ($o->items->groupBy(function($item) use ($so) {
|
||||||
|
return sprintf('%s.%s.%s.%s',$item->item_type_name,$item->price_base,$item->product->provider_ref($so),$item->taxes->pluck('description')->join('|'));
|
||||||
|
}) as $os)
|
||||||
|
{
|
||||||
|
$key = $os->first();
|
||||||
|
|
||||||
|
// Some validation
|
||||||
|
if (! ($ref=$key->product->provider_ref($so))) {
|
||||||
|
$this->error(sprintf('Accounting not defined in product [%d]',$key->product_id));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($key->taxes->count() !== 1) {
|
||||||
|
$this->error(sprintf('Cannot handle when there is not just 1 tax line [%d]',$key->id));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$c++;
|
||||||
|
$line = new \stdClass;
|
||||||
|
$line->Id = $c;
|
||||||
|
$line->DetailType = 'SalesItemLineDetail';
|
||||||
|
$line->Description = $key->item_type_name;
|
||||||
|
$line->SalesItemLineDetail = (object)[
|
||||||
|
'Qty' => $os->sum('quantity'),
|
||||||
|
'UnitPrice' => $key->price_base,
|
||||||
|
'ItemRef' => ['value'=>$ref],
|
||||||
|
// @todo It is assumed there is only 1 tax category
|
||||||
|
'TaxCodeRef' => ['value'=>$key->taxes->first()->tax->provider_ref($so)],
|
||||||
|
];
|
||||||
|
$line->Amount = $os->sum('quantity')*$key->price_base;
|
||||||
|
|
||||||
|
$lines->push($line);
|
||||||
|
}
|
||||||
|
|
||||||
|
$acc->Line = $lines;
|
||||||
|
|
||||||
|
return AccountingInvoiceUpdate::dispatchSync($to,$acc);
|
||||||
|
}
|
||||||
|
}
|
58
app/Console/Commands/AccountingInvoiceGet.php
Normal file
58
app/Console/Commands/AccountingInvoiceGet.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use GuzzleHttp\Exception\ConnectException;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
use Intuit\Exceptions\ConnectionIssueException;
|
||||||
|
|
||||||
|
use App\Models\{ProviderOauth,Site,User};
|
||||||
|
|
||||||
|
class AccountingInvoiceGet extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'accounting:invoice:get'
|
||||||
|
.' {siteid : Site ID}'
|
||||||
|
.' {provider : Provider Name}'
|
||||||
|
.' {user : User Email}'
|
||||||
|
.' {id : Invoice ID}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Get an invoice from the accounting provider';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$site = Site::findOrFail($this->argument('siteid'));
|
||||||
|
Config::set('site',$site);
|
||||||
|
|
||||||
|
$uo = User::where('email',$this->argument('user'))->singleOrFail();
|
||||||
|
|
||||||
|
$so = ProviderOauth::where('name',$this->argument('provider'))->singleOrFail();
|
||||||
|
if (! ($to=$so->token($uo)))
|
||||||
|
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = $to->API();
|
||||||
|
dump($api->getInvoiceQuery($this->argument('id')));
|
||||||
|
|
||||||
|
} catch (ConnectException|ConnectionIssueException $e) {
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
71
app/Console/Commands/AccountingItemList.php
Normal file
71
app/Console/Commands/AccountingItemList.php
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use App\Models\{Product, ProviderOauth, Site, User};
|
||||||
|
use App\Jobs\AccountingItemSync as Job;
|
||||||
|
|
||||||
|
class AccountingItemList extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'accounting:item:list'
|
||||||
|
.' {siteid : Site ID}'
|
||||||
|
.' {provider : Provider Name}'
|
||||||
|
.' {user : User Email}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Synchronise items with accounting system';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$site = Site::findOrFail($this->argument('siteid'));
|
||||||
|
Config::set('site',$site);
|
||||||
|
|
||||||
|
$so = ProviderOauth::where('name',$this->argument('provider'))->singleOrFail();
|
||||||
|
$uo = User::where('email',$this->argument('user'))->singleOrFail();
|
||||||
|
|
||||||
|
if (($x=$so->tokens->where('user_id',$uo->id))->count() !== 1)
|
||||||
|
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
||||||
|
|
||||||
|
$to = $x->pop();
|
||||||
|
|
||||||
|
// Current Products used by services
|
||||||
|
$products = Product::select(['products.*'])
|
||||||
|
->distinct('products.id')
|
||||||
|
->join('services',['services.product_id'=>'products.id'])
|
||||||
|
->where('services.active',TRUE)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$api = $so->API($to,TRUE); // @todo Remove TRUE
|
||||||
|
|
||||||
|
$acc = $api->getItems()->pluck('pid','FullyQualifiedName');
|
||||||
|
|
||||||
|
foreach ($products as $po) {
|
||||||
|
if (! $po->accounting)
|
||||||
|
$this->error(sprintf('Product [%d](%s) doesnt have accounting set',$po->id,$po->name));
|
||||||
|
|
||||||
|
elseif ($acc->has($po->accounting) === FALSE)
|
||||||
|
$this->error(sprintf('Product [%d](%s) accounting [%s] doesnt exist?',$po->id,$po->name,$po->accounting));
|
||||||
|
|
||||||
|
else
|
||||||
|
$this->info(sprintf('Product [%d](%s) set to accounting [%s]',$po->id,$po->name,$po->accounting));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
63
app/Console/Commands/AccountingPaymentGet.php
Normal file
63
app/Console/Commands/AccountingPaymentGet.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use GuzzleHttp\Exception\ConnectException;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
use Intuit\Exceptions\ConnectionIssueException;
|
||||||
|
|
||||||
|
use App\Jobs\AccountingPaymentSync as Job;
|
||||||
|
use App\Models\{ProviderOauth,Site,User};
|
||||||
|
|
||||||
|
class AccountingPaymentGet extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'accounting:payment:get'
|
||||||
|
.' {siteid : Site ID}'
|
||||||
|
.' {provider : Provider Name}'
|
||||||
|
.' {user : User Email}'
|
||||||
|
.' {id : Payment ID}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Get a payment from the accounting provider';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$site = Site::findOrFail($this->argument('siteid'));
|
||||||
|
Config::set('site',$site);
|
||||||
|
|
||||||
|
$uo = User::where('email',$this->argument('user'))->singleOrFail();
|
||||||
|
|
||||||
|
$so = ProviderOauth::where('name',$this->argument('provider'))->singleOrFail();
|
||||||
|
if (! ($to=$so->token($uo)))
|
||||||
|
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = $to->API();
|
||||||
|
$acc = $api->getPayment($this->argument('id'));
|
||||||
|
dump($acc);
|
||||||
|
|
||||||
|
} catch (ConnectException|ConnectionIssueException $e) {
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($acc)
|
||||||
|
Job::dispatchSync($to,$acc);
|
||||||
|
}
|
||||||
|
}
|
50
app/Console/Commands/AccountingPaymentSync.php
Normal file
50
app/Console/Commands/AccountingPaymentSync.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
|
use App\Models\{ProviderOauth,Site,User};
|
||||||
|
use App\Jobs\AccountingPaymentSync as Job;
|
||||||
|
|
||||||
|
class AccountingPaymentSync extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'accounting:payment:sync'
|
||||||
|
.' {siteid : Site ID}'
|
||||||
|
.' {provider : Provider Name}'
|
||||||
|
.' {user : User Email}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Synchronise payments with accounting system';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$site = Site::findOrFail($this->argument('siteid'));
|
||||||
|
Config::set('site',$site);
|
||||||
|
|
||||||
|
$uo = User::where('email',$this->argument('user'))->singleOrFail();
|
||||||
|
|
||||||
|
$so = ProviderOauth::where('name',$this->argument('provider'))->singleOrFail();
|
||||||
|
if (! ($to=$so->token($uo)))
|
||||||
|
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
||||||
|
|
||||||
|
$api = $to->API();
|
||||||
|
foreach ($api->getPayments() as $acc)
|
||||||
|
Job::dispatchSync($to,$acc);
|
||||||
|
}
|
||||||
|
}
|
51
app/Console/Commands/AccountingTaxSync.php
Normal file
51
app/Console/Commands/AccountingTaxSync.php
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
|
use App\Models\{ProviderOauth,Site,User};
|
||||||
|
use App\Jobs\AccountingTaxSync as Job;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronise TAX ids with our taxes.
|
||||||
|
*/
|
||||||
|
class AccountingTaxSync extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'accounting:tax:sync'
|
||||||
|
.' {siteid : Site ID}'
|
||||||
|
.' {provider : Provider Name}'
|
||||||
|
.' {user : User Email}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Synchronise taxes with accounting system';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$site = Site::findOrFail($this->argument('siteid'));
|
||||||
|
Config::set('site',$site);
|
||||||
|
|
||||||
|
$uo = User::where('email',$this->argument('user'))->singleOrFail();
|
||||||
|
|
||||||
|
$so = ProviderOauth::where('name',$this->argument('provider'))->singleOrFail();
|
||||||
|
if (! ($to=$so->token($uo)))
|
||||||
|
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
||||||
|
|
||||||
|
Job::dispatchSync($to);
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ namespace App\Console\Commands;
|
|||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
use App\Jobs\BroadbandTraffic as Job;
|
use App\Jobs\BroadbandTraffic as Job;
|
||||||
use App\Models\AdslSupplier;
|
use App\Models\Supplier;
|
||||||
|
|
||||||
class BroadbandTraffic extends Command
|
class BroadbandTraffic extends Command
|
||||||
{
|
{
|
||||||
@ -14,7 +14,8 @@ class BroadbandTraffic extends Command
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'broadband:traffic:import';
|
protected $signature = 'broadband:traffic:import'.
|
||||||
|
' {--s|supplier= : Supplier Name}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@ -30,7 +31,14 @@ class BroadbandTraffic extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
foreach (AdslSupplier::active()->get() as $o)
|
if ($this->option('supplier')) {
|
||||||
Job::dispatch($o);
|
$o = Supplier::where('name','like',$this->option('supplier'))->singleOrFail();
|
||||||
|
|
||||||
|
Job::dispatchSync($o->id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Supplier::active()->get() as $o)
|
||||||
|
Job::dispatchSync($o->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
44
app/Console/Commands/ImportCosts.php
Normal file
44
app/Console/Commands/ImportCosts.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
use App\Jobs\ImportCosts as Job;
|
||||||
|
use App\Models\{Site,Supplier};
|
||||||
|
|
||||||
|
class ImportCosts extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'costs:import {siteid : Site ID} {supplier : Supplier Name} {file : Filename} {date : Date}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Import Costs from file';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
if (! str_starts_with($this->argument('file'),'files/'))
|
||||||
|
throw new \Exception('Filename must start with files/');
|
||||||
|
|
||||||
|
Job::dispatchSync(
|
||||||
|
Site::findOrFail($this->argument('siteid')),
|
||||||
|
Supplier::where('name',$this->argument('supplier'))->singleOrFail(),
|
||||||
|
Carbon::create($this->argument('date')),
|
||||||
|
$this->argument('file'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,11 @@
|
|||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
use App\Models\Invoice;
|
use App\Models\{Invoice,Site};
|
||||||
|
|
||||||
class InvoiceEmail extends Command
|
class InvoiceEmail extends Command
|
||||||
{
|
{
|
||||||
@ -14,7 +15,7 @@ class InvoiceEmail extends Command
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'invoice:email {id}';
|
protected $signature = 'invoice:email {site} {id}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@ -23,16 +24,6 @@ class InvoiceEmail extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Email Invoices to be client';
|
protected $description = 'Email Invoices to be client';
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
*
|
||||||
@ -40,19 +31,21 @@ class InvoiceEmail extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
Config::set('site',Site::findOrFail($this->argument('site')));
|
||||||
|
|
||||||
$o = Invoice::findOrFail($this->argument('id'));
|
$o = Invoice::findOrFail($this->argument('id'));
|
||||||
|
|
||||||
Mail::to($o->account->user->email)->send(new \App\Mail\InvoiceEmail($o));
|
$result = Mail::to($o->account->user->email)->send(new \App\Mail\InvoiceEmail($o));
|
||||||
|
|
||||||
if (Mail::failures()) {
|
try {
|
||||||
dump('Failure?');
|
|
||||||
|
|
||||||
dump(Mail::failures());
|
|
||||||
|
|
||||||
} else {
|
|
||||||
$o->print_status = TRUE;
|
$o->print_status = TRUE;
|
||||||
$o->reminders = $o->reminders('send');
|
//$o->reminders = $o->reminders('send');
|
||||||
$o->save();
|
$o->save();
|
||||||
}
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
dd($e);
|
||||||
|
}
|
||||||
|
|
||||||
|
dump($result->getDebug());
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use App\Models\Account;
|
|
||||||
use App\Models\Invoice;
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
|
use App\Models\{Account,Invoice,Site};
|
||||||
|
|
||||||
class InvoiceGenerate extends Command
|
class InvoiceGenerate extends Command
|
||||||
{
|
{
|
||||||
@ -14,7 +14,7 @@ class InvoiceGenerate extends Command
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'invoice:generate {account?} {--p|preview : Preview} {--l|list : List Items}';
|
protected $signature = 'invoice:generate {site} {account?} {--p|preview : Preview} {--l|list : List Items}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@ -23,16 +23,6 @@ class InvoiceGenerate extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Generate Invoices to be Sent';
|
protected $description = 'Generate Invoices to be Sent';
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
*
|
||||||
@ -40,6 +30,8 @@ class InvoiceGenerate extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
Config::set('site',Site::findOrFail($this->argument('site')));
|
||||||
|
|
||||||
if ($this->argument('account'))
|
if ($this->argument('account'))
|
||||||
$accounts = collect()->push(Account::find($this->argument('account')));
|
$accounts = collect()->push(Account::find($this->argument('account')));
|
||||||
else
|
else
|
||||||
|
@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
use App\Classes\External\Payments\Ezypay;
|
use App\Classes\External\Payments\Ezypay;
|
||||||
use App\Models\{Account,Checkout,Payment};
|
use App\Jobs\PaymentsImport as Job;
|
||||||
|
|
||||||
class PaymentsEzypayImport extends Command
|
class PaymentsEzypayImport extends Command
|
||||||
{
|
{
|
||||||
@ -24,16 +23,6 @@ class PaymentsEzypayImport extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Retrieve payments from Ezypay';
|
protected $description = 'Retrieve payments from Ezypay';
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
*
|
||||||
@ -41,67 +30,6 @@ class PaymentsEzypayImport extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$poo = new Ezypay();
|
Job::dispatchSync(new Ezypay);
|
||||||
|
|
||||||
// Get our checkout IDs for this plugin
|
|
||||||
$cos = Checkout::where('plugin',config('services.ezypay.plugin'))->pluck('id');
|
|
||||||
|
|
||||||
foreach ($poo->getCustomers() as $c)
|
|
||||||
{
|
|
||||||
if ($c->BillingStatus == 'Inactive')
|
|
||||||
{
|
|
||||||
$this->info(sprintf('Ignoring INACTIVE: [%s] %s %s',$c->EzypayReferenceNumber,$c->Firstname,$c->Surname));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load Account Details from ReferenceId
|
|
||||||
$ao = Account::where('site_id',(int)substr($c->ReferenceId,0,2))
|
|
||||||
->where('id',(int)substr($c->ReferenceId,2,4))
|
|
||||||
->first();
|
|
||||||
|
|
||||||
if (! $ao)
|
|
||||||
{
|
|
||||||
$this->warn(sprintf('Missing: [%s] %s %s (%s)',$c->EzypayReferenceNumber,$c->Firstname,$c->Surname,$c->ReferenceId));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the last payment logged
|
|
||||||
$last = Carbon::createFromTimestamp(Payment::whereIN('checkout_id',$cos)->where('account_id',$ao->id)->max('date_payment'));
|
|
||||||
|
|
||||||
$o = $poo->getDebits([
|
|
||||||
'customerId'=>$c->Id,
|
|
||||||
'dateFrom'=>$last->format('Y-m-d'),
|
|
||||||
'dateTo'=>$last->addQuarter()->format('Y-m-d'),
|
|
||||||
'pageSize'=>100,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Load the payments
|
|
||||||
if ($o->count())
|
|
||||||
{
|
|
||||||
foreach ($o->reverse() as $p)
|
|
||||||
{
|
|
||||||
// If not success, ignore it.
|
|
||||||
if ($p->Status != 'Success')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$pd = Carbon::createFromFormat('Y-m-d?H:i:s.u',$p->Date);
|
|
||||||
$lp = $ao->payments->last();
|
|
||||||
|
|
||||||
if ($lp AND (($pd == $lp->date_payment) OR ($p->Id == $lp->checkout_data)))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// New Payment
|
|
||||||
$po = new Payment;
|
|
||||||
$po->site_id = 1; // @todo
|
|
||||||
$po->date_payment = $pd;
|
|
||||||
$po->checkout_id = '999'; // @todo
|
|
||||||
$po->checkout_data = $p->Id;
|
|
||||||
$po->total_amt = $p->Amount;
|
|
||||||
$ao->payments()->save($po);
|
|
||||||
|
|
||||||
$this->info(sprintf('Recorded: Payment for [%s] %s %s (%s) on %s',$c->EzypayReferenceNumber,$c->Firstname,$c->Surname,$po->id,$pd));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
use App\Classes\External\Payments\Ezypay;
|
use App\Classes\External\Payments\Ezypay;
|
||||||
@ -23,16 +24,6 @@ class PaymentsEzypayNext extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Load next payments, and ensure they cover the next invoice';
|
protected $description = 'Load next payments, and ensure they cover the next invoice';
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
*
|
||||||
@ -40,12 +31,10 @@ class PaymentsEzypayNext extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$poo = new Ezypay();
|
$poo = new Ezypay;
|
||||||
|
|
||||||
foreach ($poo->getCustomers() as $c)
|
foreach ($poo->getCustomers() as $c) {
|
||||||
{
|
if ($c->BillingStatus == 'Inactive') {
|
||||||
if ($c->BillingStatus == 'Inactive')
|
|
||||||
{
|
|
||||||
$this->info(sprintf('Ignoring INACTIVE: [%s] %s %s',$c->EzypayReferenceNumber,$c->Firstname,$c->Surname));
|
$this->info(sprintf('Ignoring INACTIVE: [%s] %s %s',$c->EzypayReferenceNumber,$c->Firstname,$c->Surname));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -53,10 +42,9 @@ class PaymentsEzypayNext extends Command
|
|||||||
// Load Account Details from ReferenceId
|
// Load Account Details from ReferenceId
|
||||||
$ao = Account::where('site_id',(int)substr($c->ReferenceId,0,2))
|
$ao = Account::where('site_id',(int)substr($c->ReferenceId,0,2))
|
||||||
->where('id',(int)substr($c->ReferenceId,2,4))
|
->where('id',(int)substr($c->ReferenceId,2,4))
|
||||||
->first();
|
->single();
|
||||||
|
|
||||||
if (! $ao)
|
if (! $ao) {
|
||||||
{
|
|
||||||
$this->warn(sprintf('Missing: [%s] %s %s (%s)',$c->EzypayReferenceNumber,$c->Firstname,$c->Surname,$c->ReferenceId));
|
$this->warn(sprintf('Missing: [%s] %s %s (%s)',$c->EzypayReferenceNumber,$c->Firstname,$c->Surname,$c->ReferenceId));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -70,12 +58,33 @@ class PaymentsEzypayNext extends Command
|
|||||||
'dateTo'=>now()->addQuarter()->format('Y-m-d'),
|
'dateTo'=>now()->addQuarter()->format('Y-m-d'),
|
||||||
])->reverse()->first();
|
])->reverse()->first();
|
||||||
|
|
||||||
|
if ($next_pay->Status !== 'Pending') {
|
||||||
|
$this->warn(sprintf('Next payment is not pending for (%s)',$ao->name));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$next_paydate = Carbon::createFromTimeString($next_pay->Date);
|
||||||
|
|
||||||
if ($next_pay->Amount < $account_due)
|
if ($next_pay->Amount < $account_due)
|
||||||
$this->warn(sprintf('Next payment for (%s) [%s] not sufficient for outstanding balance [%s]',$ao->name,number_format($next_pay->Amount,2),number_format($account_due,2)));
|
$this->warn(sprintf('Next payment on [%s] for (%s) [%s] not sufficient for outstanding balance [%s]',
|
||||||
|
$next_paydate->format('Y-m-d'),
|
||||||
|
$ao->name,
|
||||||
|
number_format($next_pay->Amount,2),
|
||||||
|
number_format($account_due,2)));
|
||||||
|
|
||||||
elseif ($next_pay->Amount > $account_due)
|
elseif ($next_pay->Amount > $account_due)
|
||||||
$this->warn(sprintf('Next payment for (%s) [%s] is too much for outstanding balance [%s]',$ao->name,number_format($next_pay->Amount,2),number_format($account_due,2)));
|
$this->warn(sprintf('Next payment on [%s] for (%s) [%s] is too much for outstanding balance [%s]',
|
||||||
|
$next_paydate->format('Y-m-d'),
|
||||||
|
$ao->name,
|
||||||
|
number_format($next_pay->Amount,2),
|
||||||
|
number_format($account_due,2)));
|
||||||
|
|
||||||
else
|
else
|
||||||
$this->info(sprintf('Next payment for (%s) [%s] will cover outstanding balance [%s]',$ao->name,number_format($next_pay->Amount,2),number_format($account_due,2)));
|
$this->info(sprintf('Next payment on [%s] for (%s) [%s] will cover outstanding balance [%s]',
|
||||||
|
$next_paydate->format('Y-m-d'),
|
||||||
|
$ao->name,
|
||||||
|
number_format($next_pay->Amount,2),
|
||||||
|
number_format($account_due,2)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
48
app/Console/Commands/ProviderTokenRefresh.php
Normal file
48
app/Console/Commands/ProviderTokenRefresh.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
|
use App\Models\{ProviderOauth,Site,User};
|
||||||
|
use App\Jobs\ProviderTokenRefresh as Job;
|
||||||
|
|
||||||
|
class ProviderTokenRefresh extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'provider:token:refresh'
|
||||||
|
.' {siteid : Site ID}'
|
||||||
|
.' {provider : Supplier Name}'
|
||||||
|
.' {user : User Email}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Refresh users access/refresh token';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$site = Site::findOrFail($this->argument('siteid'));
|
||||||
|
Config::set('site',$site);
|
||||||
|
|
||||||
|
$so = ProviderOauth::where('name',$this->argument('provider'))->singleOrFail();
|
||||||
|
$uo = User::where('email',$this->argument('user'))->singleOrFail();
|
||||||
|
|
||||||
|
if (($x=$so->tokens->where('user_id',$uo->id))->count() !== 1)
|
||||||
|
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
||||||
|
|
||||||
|
Job::dispatchSync($x->pop());
|
||||||
|
}
|
||||||
|
}
|
@ -1,261 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use QuickBooksOnline\API\Data\IPPEmailAddress;
|
|
||||||
use QuickBooksOnline\API\Data\IPPPhysicalAddress;
|
|
||||||
|
|
||||||
use App\Classes\External\Accounting\Quickbooks;
|
|
||||||
use App\Models\{Account,External\Integrations,User};
|
|
||||||
|
|
||||||
class QuickAccounts extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The name and signature of the console command.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'external:sync:accounts {--m|match : Match Display Name}';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The console command description.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'Sync Account numbers with External Sources';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the console command.
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
foreach (Integrations::active()->type('ACCOUNTING')->get() as $into)
|
|
||||||
{
|
|
||||||
switch ($into->name)
|
|
||||||
{
|
|
||||||
case 'quickbooks':
|
|
||||||
$api = new Quickbooks($into->user);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new \Exception('No handler for: ',$into-name);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($api->getCustomers(TRUE) as $r)
|
|
||||||
{
|
|
||||||
$this->info(sprintf('Checking [%s] (%s)',$r->Id,$r->DisplayName));
|
|
||||||
|
|
||||||
if ($r->Notes == 'Cash Only')
|
|
||||||
{
|
|
||||||
$this->warn(sprintf('Skipping [%s] (%s)',$r->Id,$r->DisplayName));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $this->option('match') AND (! $r->CompanyName AND (! $r->FamilyName AND ! $r->GivenName)))
|
|
||||||
{
|
|
||||||
$this->error(sprintf('No COMPANY or PERSONAL details for [%s] (%s)',$r->Id,$r->DisplayName));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->option('match')) {
|
|
||||||
$ao = Account::where('company',$r->DisplayName);
|
|
||||||
|
|
||||||
if (! $ao->count()) {
|
|
||||||
$uo = User::where('lastname',$r->FamilyName);
|
|
||||||
|
|
||||||
if ($r->GivenName)
|
|
||||||
$uo->where('firstname',$r->GivenName);
|
|
||||||
|
|
||||||
if ($uo->count() > 1 OR (! $uo->count()))
|
|
||||||
{
|
|
||||||
$this->error(sprintf('No SINGLE Users matched for [%s] (%s)',$r->Id,$r->DisplayName));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$uo = $uo->first();
|
|
||||||
$ao = $uo->accounts->where('active',TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if ($r->CompanyName) {
|
|
||||||
$ao = Account::where('company',$this->option('match') ? $r->DisplayName : $r->CompanyName);
|
|
||||||
} else {
|
|
||||||
$uo = User::where('lastname',$r->FamilyName);
|
|
||||||
|
|
||||||
if ($r->GivenName)
|
|
||||||
$uo->where('firstname',$r->GivenName);
|
|
||||||
|
|
||||||
if ($uo->count() > 1 OR (! $uo->count()))
|
|
||||||
{
|
|
||||||
$this->error(sprintf('No SINGLE matched for [%s] (%s)',$r->Id,$r->DisplayName));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$uo = $uo->first();
|
|
||||||
$ao = $uo->accounts->where('active',TRUE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $ao->count())
|
|
||||||
{
|
|
||||||
$this->error(sprintf('No Accounts matched for [%s] (%s)',$r->Id,$r->DisplayName));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($ao->count() > 1)
|
|
||||||
{
|
|
||||||
$this->error(sprintf('Too Many Accounts (%s) matched for [%s] (%s)',$ao->count(),$r->Id,$r->DisplayName));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$ao = $ao->first();
|
|
||||||
|
|
||||||
// If we are matching on DisplayName, make sure the account is updated correct for Business or Personal Accounts
|
|
||||||
$oldr = clone $r;
|
|
||||||
|
|
||||||
// @NOTE: This overwrites the ABN if it exists.
|
|
||||||
if ($r->PrimaryTaxIdentifier)
|
|
||||||
{
|
|
||||||
$this->warn(sprintf('ABN Overwrite for (%s)',$r->DisplayName));
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($ao->type)
|
|
||||||
{
|
|
||||||
case 'Business':
|
|
||||||
$r->CompanyName = $ao->company;
|
|
||||||
$r->ResaleNum = $ao->AccountId;
|
|
||||||
$r->SalesTermRef = '7'; // @todo
|
|
||||||
|
|
||||||
if ($ao->first_name)
|
|
||||||
$r->GivenName = chop($ao->user->firstname); // @todo shouldnt be required
|
|
||||||
if ($ao->last_name)
|
|
||||||
$r->FamilyName = $ao->user->lastname;
|
|
||||||
|
|
||||||
if ($ao->address1)
|
|
||||||
{
|
|
||||||
if (! $r->BillAddr)
|
|
||||||
$r->BillAddr = new IPPPhysicalAddress;
|
|
||||||
|
|
||||||
$r->BillAddr->Line1 = $ao->user->address1;
|
|
||||||
$r->BillAddr->Line2 = $ao->user->address2;
|
|
||||||
$r->BillAddr->City = $ao->user->city;
|
|
||||||
$r->BillAddr->CountrySubDivisionCode = strtoupper($ao->user->state);
|
|
||||||
$r->BillAddr->PostalCode = $ao->user->postcode;
|
|
||||||
$r->BillAddr->Country = 'Australia'; // @todo
|
|
||||||
|
|
||||||
//$r->ShipAddr = $r->BillAddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($ao->email) {
|
|
||||||
if (! $r->PrimaryEmailAddr)
|
|
||||||
$r->PrimaryEmailAddr = new IPPEmailAddress;
|
|
||||||
|
|
||||||
$r->PrimaryEmailAddr->Address = $ao->user->email;
|
|
||||||
$r->PreferredDeliveryMethod = 'Email';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $r->Balance)
|
|
||||||
$r->Active = $ao->active ? 'true' : 'false';
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'Private':
|
|
||||||
$r->CompanyName = NULL;
|
|
||||||
$r->DisplayName = sprintf('%s %s',$ao->user->lastname,$ao->user->firstname);
|
|
||||||
$r->ResaleNum = $ao->AccountId;
|
|
||||||
$r->SalesTermRef = '7'; // @todo
|
|
||||||
|
|
||||||
if ($ao->first_name)
|
|
||||||
$r->GivenName = chop($ao->user->firstname); // @todo shouldnt be required
|
|
||||||
if ($ao->last_name)
|
|
||||||
$r->FamilyName = $ao->user->lastname;
|
|
||||||
|
|
||||||
if ($ao->address1)
|
|
||||||
{
|
|
||||||
if (! $r->BillAddr)
|
|
||||||
$r->BillAddr = new IPPPhysicalAddress;
|
|
||||||
|
|
||||||
$r->BillAddr->Line1 = $ao->user->address1;
|
|
||||||
$r->BillAddr->Line2 = $ao->user->address2;
|
|
||||||
$r->BillAddr->City = $ao->user->city;
|
|
||||||
$r->BillAddr->CountrySubDivisionCode = strtoupper($ao->user->state);
|
|
||||||
$r->BillAddr->PostalCode = $ao->user->postcode;
|
|
||||||
$r->BillAddr->Country = 'Australia'; // @todo
|
|
||||||
|
|
||||||
//$r->ShipAddr = $r->BillAddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($ao->email) {
|
|
||||||
if (! $r->PrimaryEmailAddr)
|
|
||||||
$r->PrimaryEmailAddr = new IPPEmailAddress;
|
|
||||||
|
|
||||||
$r->PrimaryEmailAddr->Address = $ao->user->email;
|
|
||||||
$r->PreferredDeliveryMethod = 'Email';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $r->Balance)
|
|
||||||
$r->Active = $ao->active ? 'true' : 'false';
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new \Exception('Unhandled account type: '.$ao->type);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// If something changed, lets update it.
|
|
||||||
if (count(array_diff_assoc(object_to_array($r,FALSE),object_to_array($oldr,FALSE))))
|
|
||||||
{
|
|
||||||
$api->updateCustomer($r,[]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If external integration doesnt exist, lets create it.
|
|
||||||
if (! $ao->ExternalAccounting($into))
|
|
||||||
{
|
|
||||||
$ao->external()->attach([$into->id=>['site_id'=>1,'link'=>$r->Id]]); // @todo site_id
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the integration ID doesnt exist in the integration source, add it.
|
|
||||||
if (! $r->ResaleNum)
|
|
||||||
{
|
|
||||||
$api->updateCustomer($r,['ResaleNum'=>$ao->AccountId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If integration exist, double check the numbers match
|
|
||||||
if ($r->ResaleNum != $ao->AccountId) {
|
|
||||||
$this->warn(sprintf('Integration ID Mismatch AID [%s] ID [%s]',$ao->id,$r->Id));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function object_to_array($object,$encode=TRUE)
|
|
||||||
{
|
|
||||||
// For child arrays, we just encode
|
|
||||||
if ($encode)
|
|
||||||
return json_encode($object);
|
|
||||||
|
|
||||||
if (is_object($object)) {
|
|
||||||
return array_map(__FUNCTION__,get_object_vars($object));
|
|
||||||
} else if (is_array($object)) {
|
|
||||||
return array_map(__FUNCTION__,$object);
|
|
||||||
} else {
|
|
||||||
return $object;
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,10 +3,9 @@
|
|||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\Config;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
use App\Models\Service;
|
use App\Models\{Service,Site};
|
||||||
|
|
||||||
class ServiceList extends Command
|
class ServiceList extends Command
|
||||||
{
|
{
|
||||||
@ -16,8 +15,8 @@ class ServiceList extends Command
|
|||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'service:list'.
|
protected $signature = 'service:list'.
|
||||||
'{--a|active : Active Only}'.
|
' {--i|inactive : Include Inactive}'.
|
||||||
'{--category= : Category}'.
|
' {--t|type= : Type}'.
|
||||||
' {--f|fix : Fix start_date}';
|
' {--f|fix : Fix start_date}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,16 +26,6 @@ class ServiceList extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'List all services';
|
protected $description = 'List all services';
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
*
|
||||||
@ -44,50 +33,50 @@ class ServiceList extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
DB::listen(function($query) {
|
$header = '|%13s|%-14s|%-35s|%-40s|%8s|%17s|%12s|%12s|%12s|%12s|%14s|';
|
||||||
Log::debug('- SQL',['sql'=>$query->sql,'binding'=>$query->bindings]);
|
|
||||||
});
|
|
||||||
|
|
||||||
$this->warn(sprintf('|%10s|%-6s|%-20s|%-50s|%8s|%14s|%10s|%10s|%10s|%10s|%10s|',
|
$this->warn(sprintf($header,
|
||||||
'ID',
|
'ID',
|
||||||
'CAT',
|
'Type',
|
||||||
'Product',
|
'Product',
|
||||||
'Name',
|
'Name',
|
||||||
'active',
|
'Active',
|
||||||
'status',
|
'Status',
|
||||||
'invoice next',
|
'Next Invoice',
|
||||||
'start date',
|
'Start Date',
|
||||||
'stop date',
|
'Stop Date',
|
||||||
'connect date',
|
'Connect Date',
|
||||||
'first invoice'
|
'First Invoice'
|
||||||
));
|
));
|
||||||
|
|
||||||
foreach (Service::all() as $o) {
|
foreach (Service::withoutGlobalScope(\App\Models\Scopes\SiteScope::class)->with(['site'])->cursor() as $o) {
|
||||||
if ($this->option('active') AND ! $o->isActive())
|
if ((! $this->option('inactive')) AND ! $o->isActive())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ($this->option('category') AND $o->product->category !== $this->option('category'))
|
Config::set('site',$o->site);
|
||||||
|
|
||||||
|
if ($this->option('type') AND ($o->product->getCategoryAttribute() !== $this->option('type')))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
$c = $o->invoice_items->filter(function($item) {return $item->item_type === 0; })->sortby('date_start')->first();
|
$c = $o->invoice_items->filter(function($item) {return $item->item_type === 0; })->sortby('start_at')->first();
|
||||||
|
|
||||||
if ($this->option('fix') AND ! $o->date_start AND $c AND $c->date_start AND $o->type AND $o->type->service_connect_date AND $c->date_start->format('Y-m-d') == $o->type->service_connect_date->format('Y-m-d')) {
|
if ($this->option('fix') AND ! $o->start_at AND $c AND $c->start_at AND $o->type AND $o->type->connect_at AND $c->start_at->format('Y-m-d') == $o->type->connect_at->format('Y-m-d')) {
|
||||||
$o->date_start = $o->type->service_connect_date;
|
$o->start_at = $o->type->connect_at;
|
||||||
$o->save();
|
$o->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->info(sprintf('|%10s|%-6s|%-20s|%-50s|%8s|%14s|%10s|%10s|%10s|%10s|%10s|',
|
$this->info(sprintf($header,
|
||||||
$o->sid,
|
$o->sid,
|
||||||
$o->product->category,
|
$o->product->getCategoryNameAttribute(),
|
||||||
$o->product_name,
|
substr($o->product->getNameAttribute(),0,35),
|
||||||
$o->name_short,
|
substr($o->name_short,0,40),
|
||||||
$o->active ? 'active' : 'inactive',
|
$o->active ? 'active' : 'inactive',
|
||||||
$o->status,
|
$o->status,
|
||||||
$o->invoice_next ? $o->invoice_next->format('Y-m-d') : NULL,
|
$o->invoice_next?->format('Y-m-d'),
|
||||||
$o->date_start ? $o->date_start->format('Y-m-d') : NULL,
|
$o->start_at?->format('Y-m-d'),
|
||||||
$o->date_end ? $o->date_end->format('Y-m-d') : NULL,
|
$o->stop_at?->format('Y-m-d'),
|
||||||
($o->type AND $o->type->service_connect_date) ? $o->type->service_connect_date->format('Y-m-d') : NULL,
|
($o->type AND $o->type->connect_at) ? $o->type->connect_at->format('Y-m-d') : NULL,
|
||||||
$c ? $c->date_start->format('Y-m-d') : NULL,
|
($c && $c->date_start) ? $c->date_start->format('Y-m-d') : NULL,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
67
app/Console/Commands/SupplierAccountSync.php
Normal file
67
app/Console/Commands/SupplierAccountSync.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
|
use App\Models\{Site,Supplier,User};
|
||||||
|
|
||||||
|
class SupplierAccountSync extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'supplier:account:sync'
|
||||||
|
.' {siteid : Site ID}'
|
||||||
|
.' {supplier : Supplier Name}'
|
||||||
|
.' {--f|forceprod : Force Prod API on dev environment}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Sync accounts with a supplier';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
Config::set('site',Site::findOrFail($this->argument('siteid')));
|
||||||
|
$so = Supplier::where('name',$this->argument('supplier'))->singleOrFail();
|
||||||
|
|
||||||
|
foreach ($so->API($this->option('forceprod'))->getCustomers(['fetchall'=>true]) as $customer) {
|
||||||
|
// Check if we have this customer already (by ID)
|
||||||
|
if ($so->users->where('pivot.id',$customer->id)->count()) {
|
||||||
|
$this->info(sprintf('User already linked (%s:%s)',$customer->id,$customer->email));
|
||||||
|
|
||||||
|
} elseif ($x=User::where('email',$customer->email)->single()) {
|
||||||
|
//dump($x->suppliers->first());
|
||||||
|
if ($x->suppliers->count()) {
|
||||||
|
$this->alert(sprintf('User [%d:%s] already linked to this supplier with ID (%s)',$customer->id,$customer->email,$x->suppliers->first()->pivot->id));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$this->warn(sprintf('User [%d:%s] has same email (%s:%s) - Linked',$x->id,$x->email,$customer->id,$customer->email));
|
||||||
|
|
||||||
|
$so->users()->syncWithoutDetaching([
|
||||||
|
$x->id => [
|
||||||
|
'id'=>$customer->id,
|
||||||
|
'site_id'=>$x->site_id, // @todo See if we can have this handled automatically
|
||||||
|
'created_at'=>Carbon::create($customer->date_added),
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$this->error(sprintf('User doesnt exist with email (%s:%s)',$customer->id,$customer->email));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
app/Console/Commands/SupplierDomainSync.php
Normal file
40
app/Console/Commands/SupplierDomainSync.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
use App\Models\{Site,Supplier};
|
||||||
|
use App\Jobs\SupplierDomainSync as Job;
|
||||||
|
|
||||||
|
class SupplierDomainSync extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'supplier:domain:sync'
|
||||||
|
.' {siteid : Site ID}'
|
||||||
|
.' {supplier : Supplier Name}'
|
||||||
|
.' {--f|forceprod : Force Prod API on dev environment}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Sync domains from a supplier';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$so = Supplier::where('name',$this->argument('supplier'))->singleOrFail();
|
||||||
|
|
||||||
|
Job::dispatchSync(Site::findOrFail($this->argument('siteid')),$so,$this->option('forceprod'));
|
||||||
|
}
|
||||||
|
}
|
@ -3,10 +3,11 @@
|
|||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
|
||||||
use App\Mail\TestEmail as MailTest;
|
use App\Mail\TestEmail as MailTest;
|
||||||
use App\Models\User;
|
use App\Models\{Site,User};
|
||||||
|
|
||||||
class TestEmail extends Command
|
class TestEmail extends Command
|
||||||
{
|
{
|
||||||
@ -15,7 +16,7 @@ class TestEmail extends Command
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'test:email {id}';
|
protected $signature = 'test:email {site : Site ID} {id : User ID} {email? : Alternative Email}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@ -41,9 +42,11 @@ class TestEmail extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
Config::set('site',Site::findOrFail($this->argument('site')));
|
||||||
|
|
||||||
$uo = User::find($this->argument('id'));
|
$uo = User::find($this->argument('id'));
|
||||||
|
|
||||||
Mail::to($uo->email)
|
Mail::to($this->argument('email') ?? $uo->email)
|
||||||
->send(new MailTest($uo));
|
->send(new MailTest($uo));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,46 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console;
|
|
||||||
|
|
||||||
use Illuminate\Console\Scheduling\Schedule;
|
|
||||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
|
||||||
|
|
||||||
use App\Models\AdslSupplier;
|
|
||||||
use App\Jobs\BroadbandTraffic;
|
|
||||||
|
|
||||||
class Kernel extends ConsoleKernel
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The Artisan commands provided by your application.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $commands = [
|
|
||||||
//
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the application's command schedule.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function schedule(Schedule $schedule)
|
|
||||||
{
|
|
||||||
// @todo This needs to be more generic and dynamic
|
|
||||||
// Exetel Traffic
|
|
||||||
$schedule->job(new BroadbandTraffic(AdslSupplier::find(1)))->timezone('Australia/Melbourne')->dailyAt('10:00');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register the commands for the application.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function commands()
|
|
||||||
{
|
|
||||||
$this->load(__DIR__.'/Commands');
|
|
||||||
|
|
||||||
require base_path('routes/console.php');
|
|
||||||
}
|
|
||||||
}
|
|
40
app/Events/ProviderPaymentCreated.php
Normal file
40
app/Events/ProviderPaymentCreated.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class ProviderPaymentCreated
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
|
public array $paymentData;
|
||||||
|
public string $provider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new event instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(string $provider,array $paymentData)
|
||||||
|
{
|
||||||
|
$this->provider = $provider;
|
||||||
|
$this->paymentData = $paymentData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the channels the event should broadcast on.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Broadcasting\Channel|array
|
||||||
|
*/
|
||||||
|
public function broadcastOn()
|
||||||
|
{
|
||||||
|
return new PrivateChannel('channel-name');
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,7 @@ class Handler extends ExceptionHandler
|
|||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $dontFlash = [
|
protected $dontFlash = [
|
||||||
|
'current_password',
|
||||||
'password',
|
'password',
|
||||||
'password_confirmation',
|
'password_confirmation',
|
||||||
];
|
];
|
||||||
|
38
app/Http/Controllers/AccountingController.php
Normal file
38
app/Http/Controllers/AccountingController.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
use App\Models\{ProviderOauth,User};
|
||||||
|
|
||||||
|
class AccountingController extends Controller
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->middleware('auth');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query the accounting system and get a valid list of accounting codes
|
||||||
|
*
|
||||||
|
* @param string $provider
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public static function list(string $provider): Collection
|
||||||
|
{
|
||||||
|
// @todo This should be a variable
|
||||||
|
$uo = User::findOrFail(1);
|
||||||
|
|
||||||
|
$so = ProviderOauth::where('name',$provider)->singleOrFail();
|
||||||
|
if (! ($to=$so->token($uo)))
|
||||||
|
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
||||||
|
|
||||||
|
$api = $to->API();
|
||||||
|
|
||||||
|
return $api->getItems()
|
||||||
|
->pluck('pid','Id')
|
||||||
|
->transform(function($item,$value) { return ['id'=>$value,'value'=>$item]; })
|
||||||
|
->values();
|
||||||
|
}
|
||||||
|
}
|
@ -4,14 +4,81 @@ namespace App\Http\Controllers;
|
|||||||
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
use App\Models\{Account,Payment,PaymentItem,Service,SiteDetail};
|
use App\Models\{Account,
|
||||||
|
Charge,
|
||||||
|
Invoice,
|
||||||
|
InvoiceItem,
|
||||||
|
Payment,
|
||||||
|
PaymentItem,
|
||||||
|
Service,
|
||||||
|
SiteDetail};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The AdminController governs all routes that are prefixed with 'a/'.
|
||||||
|
*
|
||||||
|
* This is everything about the configuration of the application as a whole, or administration of a site.
|
||||||
|
*/
|
||||||
class AdminController extends Controller
|
class AdminController extends Controller
|
||||||
{
|
{
|
||||||
|
// @todo Move to reseller
|
||||||
public function service(Service $o)
|
public function service(Service $o)
|
||||||
{
|
{
|
||||||
return View('a.service',['o'=>$o]);
|
return view('theme.backend.adminlte.a.service')
|
||||||
|
->with('o',$o);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo Move to reseller
|
||||||
|
public function charge_addedit(Request $request,Charge $o)
|
||||||
|
{
|
||||||
|
if ($request->post()) {
|
||||||
|
$request->validate([
|
||||||
|
'account_id' => 'required|exists:accounts,id',
|
||||||
|
'charge_at' => 'required|date',
|
||||||
|
'service_id' => 'required|exists:services,id',
|
||||||
|
'quantity' => 'required|numeric|not_in:0',
|
||||||
|
'amount' => 'required|numeric|min:0.01',
|
||||||
|
'sweep_type' => 'required|numeric|in:'.implode(',',array_keys(Charge::sweep)),
|
||||||
|
'type' => 'required|numeric|in:'.implode(',',array_keys(InvoiceItem::type)),
|
||||||
|
'taxable' => 'nullable|boolean',
|
||||||
|
'description' => 'nullable|string|max:128',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (! $o->exists) {
|
||||||
|
$o->site_id = config('site')->site_id;
|
||||||
|
$o->user_id = Auth::id();
|
||||||
|
$o->active = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$o->forceFill($request->only(['account_id','charge_at','service_id','quantity','amount','sweep_type','type','taxable','description']));
|
||||||
|
$o->save();
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('success','Charge recorded: '.$o->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('theme.backend.adminlte.a.charge.addedit')
|
||||||
|
->with('o',$o);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo Move to reseller
|
||||||
|
public function charge_pending_account(Request $request,Account $o)
|
||||||
|
{
|
||||||
|
return view('theme.backend.adminlte.a.charge.widgets.pending')
|
||||||
|
->with('list',$o->charges->where('active',TRUE)->where('processed',NULL)->except($request->exclude));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List unprocessed charges
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
// @todo Move to reseller
|
||||||
|
public function charge_unprocessed()
|
||||||
|
{
|
||||||
|
return view('theme.backend.adminlte.a.charge.unprocessed');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -21,56 +88,98 @@ class AdminController extends Controller
|
|||||||
* @param Payment $o
|
* @param Payment $o
|
||||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
|
||||||
*/
|
*/
|
||||||
public function pay_add(Request $request,Payment $o)
|
// @todo Move to reseller
|
||||||
|
public function pay_addedit(Request $request,Payment $o)
|
||||||
{
|
{
|
||||||
if ($request->post()) {
|
if ($request->post()) {
|
||||||
|
|
||||||
$validation = $request->validate([
|
$validation = $request->validate([
|
||||||
'account_id' => 'required|exists:ab_account,id',
|
'account_id' => 'required|exists:accounts,id',
|
||||||
'date_payment' => 'required|date',
|
'paid_at' => 'required|date',
|
||||||
'checkout_id' => 'required|exists:ab_checkout,id',
|
'checkout_id' => 'required|exists:checkouts,id',
|
||||||
'total_amt' => 'required|numeric|min:0.01',
|
'total_amt' => 'required|numeric|min:0.01',
|
||||||
'fees_amt' => 'nullable|numeric|lt:total_amt',
|
'fees_amt' => 'nullable|numeric|lt:total_amt',
|
||||||
'source_id' => 'nullable|exists:ab_account,id',
|
'source_id' => 'nullable|exists:accounts,id',
|
||||||
'pending' => 'nullable|boolean',
|
'pending' => 'nullable|boolean',
|
||||||
'notes' => 'nullable|string',
|
'notes' => 'nullable|string',
|
||||||
'ip' => 'nullable|ip',
|
'ip' => 'nullable|ip',
|
||||||
'invoices' => ['nullable','array',function ($attribute,$value,$fail) use ($request) {
|
'invoices' => ['required','array',function ($attribute,$value,$fail) use ($request) {
|
||||||
if (collect($value)->sum() > $request->post('total_amt'))
|
if (collect($value)->sum('id') > $request->post('total_amt'))
|
||||||
$fail('Allocation is greater than payment total.');
|
$fail('Allocation is greater than payment total.');
|
||||||
}],
|
}],
|
||||||
'invoices.*.id' => 'nullable|exists:ab_invoice,id',
|
'invoices.*.id' => ['required',function ($attribute,$value,$fail) {
|
||||||
|
if (! Invoice::exists(str_replace(str_replace($attribute,'invoice\.','',),'.id','')))
|
||||||
|
$fail('Invoice doesnt exist in DB');
|
||||||
|
}],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$oo = new Payment;
|
if (! $o->exists) {
|
||||||
$oo->forceFill($request->only(['account_id','date_payment','checkout_id','checkout_id','total_amt','fees_amt','source_id','pending','notes','ip']));
|
$o->site_id = config('site')->site_id;
|
||||||
$oo->site_id = config('SITE')->site_id;
|
$o->active = TRUE;
|
||||||
$oo->save();
|
}
|
||||||
|
|
||||||
|
$o->forceFill($request->only(['account_id','paid_at','checkout_id','total_amt','fees_amt','source_id','pending','notes','ip']));
|
||||||
|
$o->save();
|
||||||
|
|
||||||
foreach ($validation['invoices'] as $id => $amount) {
|
foreach ($validation['invoices'] as $id => $amount) {
|
||||||
$ooo = new PaymentItem;
|
// See if we already have a payment item that we need to update
|
||||||
$ooo->invoice_id = $id;
|
$items = $o->items->filter(function($item) use ($id) { return $item->invoice_id == $id; });
|
||||||
$ooo->alloc_amt = $amount;
|
|
||||||
$ooo->site_id = config('SITE')->site_id;
|
if ($items->count() == 1) {
|
||||||
$oo->items()->save($ooo);
|
$oo = $items->pop();
|
||||||
|
if ($amount['id'] == 0) {
|
||||||
|
$oo->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()
|
} else {
|
||||||
->with('success','Payment recorded');
|
$oo = new PaymentItem;
|
||||||
|
$oo->invoice_id = $id;
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('a.payment.add')
|
$oo->amount = ($oo->invoice->due >= 0) && ($oo->invoice->due-$amount['id'] >= 0) ? $amount['id'] : 0;
|
||||||
|
|
||||||
|
// If the amount is empty, ignore it.
|
||||||
|
if (! $oo->amount)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$oo->site_id = config('site')->site_id;
|
||||||
|
$oo->active = TRUE;
|
||||||
|
$o->items()->save($oo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('success','Payment recorded: '.$o->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('theme.backend.adminlte.a.payment.addedit')
|
||||||
->with('o',$o);
|
->with('o',$o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List unapplied payments
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
// @todo Move to reseller
|
||||||
|
public function pay_unapplied()
|
||||||
|
{
|
||||||
|
return view('theme.backend.adminlte.a.payment.unapplied');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show a list of invoices to apply payments to
|
* Show a list of invoices to apply payments to
|
||||||
*
|
*
|
||||||
|
* @param Request $request
|
||||||
* @param Account $o
|
* @param Account $o
|
||||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||||
*/
|
*/
|
||||||
public function pay_invoices(Account $o)
|
// @todo Move to reseller
|
||||||
|
public function pay_invoices(Request $request,Account $o)
|
||||||
{
|
{
|
||||||
return view('a.payment.widgets.invoices')
|
return view('theme.backend.adminlte.a.payment.widgets.invoices')
|
||||||
|
->with('pid',$request->pid)
|
||||||
->with('o',$o);
|
->with('o',$o);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,13 +194,13 @@ class AdminController extends Controller
|
|||||||
{
|
{
|
||||||
if ($request->post()) {
|
if ($request->post()) {
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
'site_name' => 'required|string|max:255',
|
'site_name' => 'required|string|min:2|max:255',
|
||||||
'site_email' => 'required|string|email|max:255',
|
'site_email' => 'required|string|email|max:255',
|
||||||
'site_address1' => 'required|string|max:255',
|
'site_address1' => 'required|string|min:2|max:255',
|
||||||
'site_address2' => 'nullable|string|max:255',
|
'site_address2' => 'nullable|string|min:2|max:255',
|
||||||
'site_city' => 'required|string|max:64',
|
'site_city' => 'required|string|min:2|max:64',
|
||||||
'site_state' => 'required|string|max:32',
|
'site_state' => 'required|string|min:2|max:32',
|
||||||
'site_postcode' => 'required|string|max:8',
|
'site_postcode' => 'required|string|min:2|max:8',
|
||||||
'site_description' => 'nullable|string|min:5',
|
'site_description' => 'nullable|string|min:5',
|
||||||
'site_phone' => 'nullable|regex:/[0-9 ]+/|min:6|max:12',
|
'site_phone' => 'nullable|regex:/[0-9 ]+/|min:6|max:12',
|
||||||
'site_fax' => 'nullable|regex:/[0-9 ]+/|min:6|max:12',
|
'site_fax' => 'nullable|regex:/[0-9 ]+/|min:6|max:12',
|
||||||
@ -102,7 +211,7 @@ class AdminController extends Controller
|
|||||||
'email_logo' => 'nullable|image',
|
'email_logo' => 'nullable|image',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$site = config('SITE');
|
$site = config('site');
|
||||||
|
|
||||||
// @todo - not currently rendered in the home page
|
// @todo - not currently rendered in the home page
|
||||||
$validated['social'] = [];
|
$validated['social'] = [];
|
||||||
@ -130,10 +239,11 @@ class AdminController extends Controller
|
|||||||
$site->details()->save($oo);
|
$site->details()->save($oo);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()
|
return redirect()
|
||||||
|
->back()
|
||||||
->with('success','Settings saved');
|
->with('success','Settings saved');
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('a.setup');
|
return view('theme.backend.adminlte.theme.backend.adminlte.a.setup');
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,8 +4,6 @@ namespace App\Http\Controllers\Auth;
|
|||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Facades\Password;
|
|
||||||
|
|
||||||
class ForgotPasswordController extends Controller
|
class ForgotPasswordController extends Controller
|
||||||
{
|
{
|
||||||
@ -21,29 +19,4 @@ class ForgotPasswordController extends Controller
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use SendsPasswordResetEmails;
|
use SendsPasswordResetEmails;
|
||||||
|
|
||||||
public function showLinkRequestForm()
|
|
||||||
{
|
|
||||||
return view('adminlte::auth.passwords.email');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function sendResetLinkEmail(Request $request)
|
|
||||||
{
|
|
||||||
$this->validateEmail($request);
|
|
||||||
|
|
||||||
// If the account is not active, or doesnt exist, we'll send a fake "sent" message.
|
|
||||||
if (! ($x=$this->broker()->getUser($this->credentials($request))) || (! $x->active))
|
|
||||||
return $this->sendResetLinkResponse($request, Password::RESET_LINK_SENT);
|
|
||||||
|
|
||||||
// We will send the password reset link to this user. Once we have attempted
|
|
||||||
// to send the link, we will examine the response then see the message we
|
|
||||||
// need to show to the user. Finally, we'll send out a proper response.
|
|
||||||
$response = $this->broker()->sendResetLink(
|
|
||||||
$this->credentials($request)
|
|
||||||
);
|
|
||||||
|
|
||||||
return $response == Password::RESET_LINK_SENT
|
|
||||||
? $this->sendResetLinkResponse($request, $response)
|
|
||||||
: $this->sendResetLinkFailedResponse($request, $response);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
namespace App\Http\Controllers\Auth;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Providers\RouteServiceProvider;
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
@ -30,7 +28,7 @@ class LoginController extends Controller
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $redirectTo = RouteServiceProvider::HOME;
|
protected $redirectTo = '/home';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new controller instance.
|
* Create a new controller instance.
|
||||||
@ -39,7 +37,8 @@ class LoginController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->middleware('guest')->except('logout');
|
$this->middleware('auth')
|
||||||
|
->only('logout');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function login(Request $request)
|
public function login(Request $request)
|
||||||
@ -73,6 +72,7 @@ class LoginController extends Controller
|
|||||||
if (file_exists('login_note.txt'))
|
if (file_exists('login_note.txt'))
|
||||||
$login_note = file_get_contents('login_note.txt');
|
$login_note = file_get_contents('login_note.txt');
|
||||||
|
|
||||||
return view('adminlte::auth.login')->with('login_note',$login_note);
|
return view('adminlte::auth.login')
|
||||||
|
->with('login_note',$login_note);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Auth;
|
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Providers\RouteServiceProvider;
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Foundation\Auth\RegistersUsers;
|
|
||||||
use Illuminate\Support\Facades\Hash;
|
|
||||||
use Illuminate\Support\Facades\Validator;
|
|
||||||
|
|
||||||
class RegisterController extends Controller
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Register Controller
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| This controller handles the registration of new users as well as their
|
|
||||||
| validation and creation. By default this controller uses a trait to
|
|
||||||
| provide this functionality without requiring any additional code.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
use RegistersUsers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Where to redirect users after registration.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $redirectTo = RouteServiceProvider::HOME;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new controller instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->middleware('guest');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a validator for an incoming registration request.
|
|
||||||
*
|
|
||||||
* @param array $data
|
|
||||||
* @return \Illuminate\Contracts\Validation\Validator
|
|
||||||
*/
|
|
||||||
protected function validator(array $data)
|
|
||||||
{
|
|
||||||
return Validator::make($data, [
|
|
||||||
'name' => ['required', 'string', 'min:3', 'max:255'],
|
|
||||||
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
|
|
||||||
'password' => ['required', 'string', 'min:8', 'confirmed'],
|
|
||||||
'token' => 'required|doorman:email',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new user instance after a valid registration.
|
|
||||||
*
|
|
||||||
* @param array $data
|
|
||||||
* @return User
|
|
||||||
*/
|
|
||||||
protected function create(array $data): User
|
|
||||||
{
|
|
||||||
$fields = [
|
|
||||||
'name' => $data['name'],
|
|
||||||
'email' => $data['email'],
|
|
||||||
'password' => Hash::make($data['password']),
|
|
||||||
];
|
|
||||||
|
|
||||||
if (config('auth.providers.users.field','email') === 'username' && isset($data['username'])) {
|
|
||||||
$fields['username'] = $data['username'];
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Doorman::redeem($data['token'],$data['email']);
|
|
||||||
|
|
||||||
// @todo Want to direct or display an appropriate error message (although the form validation does it anyway).
|
|
||||||
} catch (DoormanException $e) {
|
|
||||||
redirect('/error');
|
|
||||||
abort(403);
|
|
||||||
}
|
|
||||||
|
|
||||||
return User::create($fields);
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,9 +3,7 @@
|
|||||||
namespace App\Http\Controllers\Auth;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Providers\RouteServiceProvider;
|
|
||||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||||
use Illuminate\Http\Request;
|
|
||||||
|
|
||||||
class ResetPasswordController extends Controller
|
class ResetPasswordController extends Controller
|
||||||
{
|
{
|
||||||
@ -27,22 +25,5 @@ class ResetPasswordController extends Controller
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $redirectTo = RouteServiceProvider::HOME;
|
protected $redirectTo = '/home';
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new controller instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->middleware('guest');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function showResetForm(Request $request, $token = null)
|
|
||||||
{
|
|
||||||
return view('adminlte::auth.passwords.reset')->with(
|
|
||||||
['token' => $token, 'email' => $request->email]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Auth;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
@ -10,26 +11,27 @@ use Laravel\Socialite\Facades\Socialite;
|
|||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Mail\SocialLink;
|
use App\Mail\SocialLink;
|
||||||
use App\Models\Oauth;
|
use App\Models\{ProviderOauth,ProviderToken,User,UserOauth};
|
||||||
use App\Models\AccountOauth;
|
|
||||||
use App\Models\User;
|
|
||||||
use App\Providers\RouteServiceProvider;
|
|
||||||
|
|
||||||
class SocialLoginController extends Controller
|
class SocialLoginController extends Controller
|
||||||
{
|
{
|
||||||
public function redirectToProvider($provider)
|
public function redirectToProvider($provider)
|
||||||
{
|
{
|
||||||
return Socialite::with($provider)->redirect();
|
return Socialite::with($provider)
|
||||||
|
->redirect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleProviderCallback($provider)
|
public function handleProviderCallback($provider)
|
||||||
{
|
{
|
||||||
$openiduser = Socialite::with($provider)->user();
|
$openiduser = Socialite::with($provider)->user();
|
||||||
|
|
||||||
$oo = Oauth::firstOrCreate(['name'=>$provider,'active'=>TRUE]);
|
if (! $openiduser)
|
||||||
|
return redirect('/home')->with('error','No user details obtained.');
|
||||||
|
|
||||||
|
$oo = ProviderOauth::firstOrCreate(['name'=>$provider,'active'=>TRUE]);
|
||||||
|
|
||||||
// See if this user has connected and linked previously
|
// See if this user has connected and linked previously
|
||||||
$aoo = $oo->accounts->where('userid',$openiduser->id);
|
$aoo = $oo->users->where('userid',$openiduser->id);
|
||||||
|
|
||||||
if ($aoo->count() == 1) {
|
if ($aoo->count() == 1) {
|
||||||
$aoo = $aoo->first();
|
$aoo = $aoo->first();
|
||||||
@ -59,10 +61,10 @@ class SocialLoginController extends Controller
|
|||||||
|
|
||||||
// See if their is an account with this email address
|
// See if their is an account with this email address
|
||||||
if ($uo->count() == 1) {
|
if ($uo->count() == 1) {
|
||||||
$aoo = new AccountOauth;
|
$aoo = new UserOauth;
|
||||||
$aoo->userid = $openiduser->id;
|
$aoo->userid = $openiduser->id;
|
||||||
$aoo->oauth_data = $openiduser->user;
|
$aoo->oauth_data = $openiduser->user;
|
||||||
$oo->accounts()->save($aoo);
|
$oo->users()->save($aoo);
|
||||||
|
|
||||||
return $this->link($provider,$aoo,$uo->first());
|
return $this->link($provider,$aoo,$uo->first());
|
||||||
|
|
||||||
@ -75,19 +77,47 @@ class SocialLoginController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->intended(RouteServiceProvider::HOME);
|
return redirect()
|
||||||
|
->intended('/home');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleBearerTokenCallback($provider)
|
||||||
|
{
|
||||||
|
$openiduser = Socialite::with($provider)->user();
|
||||||
|
|
||||||
|
if (! $openiduser)
|
||||||
|
return redirect('/home')->with('error','No user details obtained.');
|
||||||
|
|
||||||
|
$po = ProviderOauth::where('name',$provider)->singleOrFail();
|
||||||
|
|
||||||
|
$uoo = ProviderToken::where('user_id',Auth::id())->where('provider_oauth_id',$po->id)->firstOrNew();
|
||||||
|
|
||||||
|
$uoo->user_id = Auth::id();
|
||||||
|
$uoo->access_token = $openiduser->token;
|
||||||
|
$uoo->access_token_expires_at = Carbon::now()->addSeconds($openiduser->expiresIn);
|
||||||
|
$uoo->refresh_token = $openiduser->refreshToken;
|
||||||
|
$uoo->refresh_token_expires_at = Carbon::now()->addSeconds($openiduser->refresh_token_expires_in);
|
||||||
|
$uoo->realm_id = $openiduser->realmid;
|
||||||
|
|
||||||
|
$po->tokens()->save($uoo);
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->intended('/home')
|
||||||
|
->with('success','Token refreshed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We have identified the user and oauth, just need them to confirm the link
|
* We have identified the user and oauth, just need them to confirm the link
|
||||||
*
|
*
|
||||||
* @param $provider
|
* @param $provider
|
||||||
|
* @param UserOauth $ao
|
||||||
* @param User $uo
|
* @param User $uo
|
||||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
* @return \Illuminate\View\View
|
||||||
*/
|
*/
|
||||||
public function link($provider,AccountOauth $ao,User $uo)
|
public function link($provider,UserOauth $ao,User $uo): \Illuminate\View\View
|
||||||
{
|
{
|
||||||
Mail::to($uo->email)->send(new SocialLink($ao));
|
// @note If this is sent now (send()), it results in the caller to be executed a second time (handleProviderCallback()).
|
||||||
|
Mail::to($uo->email)->queue(new SocialLink($ao));
|
||||||
|
|
||||||
return view('auth.social_link')
|
return view('auth.social_link')
|
||||||
->with('oauthid',$ao->id)
|
->with('oauthid',$ao->id)
|
||||||
@ -97,7 +127,7 @@ class SocialLoginController extends Controller
|
|||||||
public function linkcomplete(Request $request,$provider)
|
public function linkcomplete(Request $request,$provider)
|
||||||
{
|
{
|
||||||
// Load our oauth id
|
// Load our oauth id
|
||||||
$aoo = AccountOauth::findOrFail($request->post('oauthid'));
|
$aoo = UserOauth::findOrFail($request->post('oauthid'));
|
||||||
|
|
||||||
// Check our email matches
|
// Check our email matches
|
||||||
if (Arr::get($aoo->oauth_data,'email','invalid') !== $request->post('email'))
|
if (Arr::get($aoo->oauth_data,'email','invalid') !== $request->post('email'))
|
||||||
@ -112,8 +142,9 @@ class SocialLoginController extends Controller
|
|||||||
|
|
||||||
$aoo->user_id = $uo->id;
|
$aoo->user_id = $uo->id;
|
||||||
$aoo->save();
|
$aoo->save();
|
||||||
Auth::login($uo,FALSE);
|
Auth::login($uo);
|
||||||
|
|
||||||
return redirect()->intended(RouteServiceProvider::HOME);
|
return redirect()
|
||||||
|
->intended('/home');
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,13 +2,42 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\Invoice;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
use App\Models\Checkout;
|
use App\Http\Requests\CheckoutAddEdit;
|
||||||
|
use App\Models\{Checkout,Invoice};
|
||||||
|
|
||||||
class CheckoutController extends Controller
|
class CheckoutController extends Controller
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Update a suppliers details
|
||||||
|
*
|
||||||
|
* @param CheckoutAddEdit $request
|
||||||
|
* @param Checkout $o
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function addedit(CheckoutAddEdit $request,Checkout $o)
|
||||||
|
{
|
||||||
|
$this->middleware(['auth','wholesaler']);
|
||||||
|
|
||||||
|
foreach ($request->except(['_token','active','submit']) as $key => $item)
|
||||||
|
$o->{$key} = $item;
|
||||||
|
|
||||||
|
$o->active = (bool)$request->active;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$o->save();
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return redirect()->back()->withErrors($e->getMessage())->withInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('success','Payment saved');
|
||||||
|
}
|
||||||
|
|
||||||
public function cart_invoice(Request $request,Invoice $o=NULL)
|
public function cart_invoice(Request $request,Invoice $o=NULL)
|
||||||
{
|
{
|
||||||
if ($o) {
|
if ($o) {
|
||||||
@ -16,9 +45,10 @@ class CheckoutController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (! $request->session()->get('invoice.cart'))
|
if (! $request->session()->get('invoice.cart'))
|
||||||
return redirect()->to('u/home');
|
return redirect()
|
||||||
|
->to('u/home');
|
||||||
|
|
||||||
return View('u.invoice.cart')
|
return view('theme.backend.adminlte.u.invoice.cart')
|
||||||
->with('invoices',Invoice::find(array_values($request->session()->get('invoice.cart'))));
|
->with('invoices',Invoice::find(array_values($request->session()->get('invoice.cart'))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,8 +57,29 @@ class CheckoutController extends Controller
|
|||||||
return $o->fee($request->post('total',0));
|
return $o->fee($request->post('total',0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a specific invoice for the user
|
||||||
|
*
|
||||||
|
* @return View
|
||||||
|
*/
|
||||||
|
public function home(): View
|
||||||
|
{
|
||||||
|
return view('theme.backend.adminlte.payment.home');
|
||||||
|
}
|
||||||
|
|
||||||
public function pay(Request $request,Checkout $o)
|
public function pay(Request $request,Checkout $o)
|
||||||
{
|
{
|
||||||
return redirect('pay/paypal/authorise');
|
return redirect('pay/paypal/authorise');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View details on a specific payment method
|
||||||
|
*
|
||||||
|
* @param Checkout $o
|
||||||
|
* @return View
|
||||||
|
*/
|
||||||
|
public function view(Checkout $o): View
|
||||||
|
{
|
||||||
|
return view('theme.backend.adminlte.payment.view',['o'=>$o]);
|
||||||
|
}
|
||||||
}
|
}
|
@ -2,12 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
|
||||||
use Illuminate\Routing\Controller as BaseController;
|
|
||||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
|
|
||||||
class Controller extends BaseController
|
abstract class Controller extends \Illuminate\Routing\Controller
|
||||||
{
|
{
|
||||||
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
use AuthorizesRequests, ValidatesRequests;
|
||||||
}
|
}
|
@ -4,11 +4,9 @@ namespace App\Http\Controllers;
|
|||||||
|
|
||||||
use Clarkeash\Doorman\Exceptions\{DoormanException,ExpiredInviteCode};
|
use Clarkeash\Doorman\Exceptions\{DoormanException,ExpiredInviteCode};
|
||||||
use Clarkeash\Doorman\Facades\Doorman;
|
use Clarkeash\Doorman\Facades\Doorman;
|
||||||
use Illuminate\Contracts\View\Factory;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use Barryvdh\Snappy\Facades\SnappyPdf as PDF;
|
|
||||||
|
|
||||||
use App\Models\{Invoice,Service,User};
|
use App\Models\{Invoice,Service,User};
|
||||||
|
|
||||||
@ -22,60 +20,19 @@ use App\Models\{Invoice,Service,User};
|
|||||||
*/
|
*/
|
||||||
class HomeController extends Controller
|
class HomeController extends Controller
|
||||||
{
|
{
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->middleware('auth');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logged in users home page
|
* Logged in users home page
|
||||||
*
|
*
|
||||||
* @return Factory|View
|
* @param User $o
|
||||||
|
* @return View
|
||||||
*/
|
*/
|
||||||
public function home(User $o): View
|
public function home(User $o): View
|
||||||
{
|
{
|
||||||
// If we are passed a user to view, we'll open up their home page.
|
if (! $o->exists)
|
||||||
if ($o->exists) {
|
|
||||||
$o->load(['accounts','services']);
|
|
||||||
return View('u.home',['o'=>$o]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If User was null, then test and see what type of logged on user we have
|
|
||||||
$o = Auth::user();
|
$o = Auth::user();
|
||||||
|
|
||||||
switch (Auth::user()->role()) {
|
return view('theme.backend.adminlte.home')
|
||||||
case 'customer':
|
->with(['o'=>$o]);
|
||||||
return View('u.home',['o'=>$o]);
|
|
||||||
|
|
||||||
case 'reseller':
|
|
||||||
case 'wholesaler':
|
|
||||||
return View('r.home',['o'=>$o]);
|
|
||||||
|
|
||||||
default:
|
|
||||||
abort(404,'Unknown role: '.$o->role());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render a specific invoice for the user
|
|
||||||
*
|
|
||||||
* @param Invoice $o
|
|
||||||
* @return View
|
|
||||||
*/
|
|
||||||
public function invoice(Invoice $o): View
|
|
||||||
{
|
|
||||||
return View('u.invoice.home',['o'=>$o]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the invoice in PDF format, ready to download
|
|
||||||
*
|
|
||||||
* @param Invoice $o
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function invoice_pdf(Invoice $o)
|
|
||||||
{
|
|
||||||
return PDF::loadView('u.invoice.home',['o'=>$o])->stream(sprintf('%s.pdf',$o->invoice_account_id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -101,18 +58,7 @@ class HomeController extends Controller
|
|||||||
abort(404);
|
abort(404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->invoice_pdf($o);
|
return $this->invoice($o);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return details on the users service
|
|
||||||
*
|
|
||||||
* @param Service $o
|
|
||||||
* @return View
|
|
||||||
*/
|
|
||||||
public function service(Service $o): View
|
|
||||||
{
|
|
||||||
return View('u.service.home',['o'=>$o]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -122,6 +68,7 @@ class HomeController extends Controller
|
|||||||
* @param Service $o
|
* @param Service $o
|
||||||
* @param string $status
|
* @param string $status
|
||||||
* @return \Illuminate\Http\RedirectResponse
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
public function service_progress(Service $o,string $status)
|
public function service_progress(Service $o,string $status)
|
||||||
{
|
{
|
||||||
|
55
app/Http/Controllers/InvoiceController.php
Normal file
55
app/Http/Controllers/InvoiceController.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Clarkeash\Doorman\Exceptions\{ExpiredInviteCode,InvalidInviteCode,NotYourInviteCode};
|
||||||
|
use Clarkeash\Doorman\Facades\Doorman;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
use Barryvdh\Snappy\Facades\SnappyPdf as PDF;
|
||||||
|
|
||||||
|
use App\Models\Invoice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class InvoiceController
|
||||||
|
* This controller manages invoices
|
||||||
|
*
|
||||||
|
* The methods to this controller should be projected by the route
|
||||||
|
*
|
||||||
|
* @package App\Http\Controllers
|
||||||
|
*/
|
||||||
|
class InvoiceController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Return the invoice in PDF format, ready to download
|
||||||
|
*
|
||||||
|
* @param Invoice $o
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function pdf(Invoice $o)
|
||||||
|
{
|
||||||
|
return PDF::loadView('theme.backend.adminlte.u.invoice.home',['o'=>$o])
|
||||||
|
->stream(sprintf('%s.pdf',$o->sid));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a specific invoice for the user
|
||||||
|
*
|
||||||
|
* @param Invoice $o
|
||||||
|
* @param string|null $code
|
||||||
|
* @return View
|
||||||
|
*/
|
||||||
|
public function view(Invoice $o,string $code=NULL): View
|
||||||
|
{
|
||||||
|
if ($code) {
|
||||||
|
try {
|
||||||
|
Doorman::redeem($code,$o->account->user->email);
|
||||||
|
|
||||||
|
} catch (ExpiredInviteCode|InvalidInviteCode|NotYourInviteCode $e) {
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('theme.backend.adminlte.invoice.view')
|
||||||
|
->with('o',$o);
|
||||||
|
}
|
||||||
|
}
|
@ -1,27 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use Image;
|
|
||||||
|
|
||||||
class MediaController extends Controller
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Create a generic image
|
|
||||||
*
|
|
||||||
* @param $width
|
|
||||||
* @param $height
|
|
||||||
* @param string $color
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function image($width,$height,$color='#ccc') {
|
|
||||||
$io = Image::canvas($width,$height,$color);
|
|
||||||
$io->text(sprintf('IMAGE-%sx%s',$width,$height),$width/2,$height/2,function($font) {
|
|
||||||
$font->file(5);
|
|
||||||
$font->align('center');
|
|
||||||
$font->valign('middle');
|
|
||||||
});
|
|
||||||
|
|
||||||
return $io->response();
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,7 +3,6 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Igaster\LaravelTheme\Facades\Theme;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
@ -14,33 +13,36 @@ use App\Models\{Account,Product,Service,User};
|
|||||||
|
|
||||||
class OrderController extends Controller
|
class OrderController extends Controller
|
||||||
{
|
{
|
||||||
|
// @todo To check
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->middleware('auth');
|
$this->middleware('auth');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @todo To check
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
return view('order');
|
return view('theme.backend.adminlte.order.home');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @todo To check
|
||||||
public function product_order(Product $o)
|
public function product_order(Product $o)
|
||||||
{
|
{
|
||||||
Theme::set('metronic-fe');
|
return view('theme.backend.adminlte.order.widget.order')
|
||||||
|
->with('o',$o);
|
||||||
return view('widgets.product_order',['o'=>$o]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @todo To check
|
||||||
public function product_info(Product $o)
|
public function product_info(Product $o)
|
||||||
{
|
{
|
||||||
Theme::set('metronic-fe');
|
return view('theme.backend.adminlte.order.widget.info')
|
||||||
|
->with('o',$o);
|
||||||
return view('widgets.product_description',['o'=>$o]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @todo To check
|
||||||
public function submit(Request $request)
|
public function submit(Request $request)
|
||||||
{
|
{
|
||||||
Validator::make($request->all(),['product_id'=>'required|exists:ab_product,id'])
|
Validator::make($request->all(),['product_id'=>'required|exists:products,id'])
|
||||||
// Reseller
|
// Reseller
|
||||||
->sometimes('account_id','required|email',function($input) use ($request) {
|
->sometimes('account_id','required|email',function($input) use ($request) {
|
||||||
return is_null($input->account_id) AND is_null($input->order_email_manual);
|
return is_null($input->account_id) AND is_null($input->order_email_manual);
|
||||||
@ -59,7 +61,7 @@ class OrderController extends Controller
|
|||||||
$po = Product::findOrFail($request->input('product_id'));
|
$po = Product::findOrFail($request->input('product_id'));
|
||||||
|
|
||||||
// Check we have the custom attributes for the product
|
// Check we have the custom attributes for the product
|
||||||
$options = $po->orderValidation($request);
|
$order = $po->orderValidation($request);
|
||||||
|
|
||||||
if ($request->input('order_email_manual')) {
|
if ($request->input('order_email_manual')) {
|
||||||
$uo = User::firstOrNew(['email'=>$request->input('order_email_manual')]);
|
$uo = User::firstOrNew(['email'=>$request->input('order_email_manual')]);
|
||||||
@ -67,12 +69,13 @@ class OrderController extends Controller
|
|||||||
// If this is a new client
|
// If this is a new client
|
||||||
if (! $uo->exists) {
|
if (! $uo->exists) {
|
||||||
// @todo Make this automatic
|
// @todo Make this automatic
|
||||||
$uo->site_id = config('SITE')->site_id;
|
$uo->site_id = config('site')->site_id;
|
||||||
$uo->active = FALSE;
|
$uo->active = FALSE;
|
||||||
$uo->firstname = '';
|
$uo->firstname = '';
|
||||||
$uo->lastname = '';
|
$uo->lastname = '';
|
||||||
$uo->country_id = config('SITE')->country_id; // @todo This might be wrong
|
$uo->country_id = config('site')->country_id; // @todo This might be wrong
|
||||||
$uo->parent_id = Auth::id() ?: 1; // @todo This should be configured to a default user
|
$uo->parent_id = Auth::id() ?: 1; // @todo This should be configured to a default user
|
||||||
|
$uo->language_id = config('site')->language_id; // @todo This might be wrong
|
||||||
$uo->active = 1;
|
$uo->active = 1;
|
||||||
$uo->save();
|
$uo->save();
|
||||||
}
|
}
|
||||||
@ -83,10 +86,8 @@ class OrderController extends Controller
|
|||||||
$ao = new Account;
|
$ao = new Account;
|
||||||
//$ao->id = Account::NextId();
|
//$ao->id = Account::NextId();
|
||||||
// @todo Make this automatic
|
// @todo Make this automatic
|
||||||
$ao->site_id = config('SITE')->site_id;
|
$ao->site_id = config('site')->site_id;
|
||||||
$ao->country_id = config('SITE')->country_id; // @todo This might be wrong
|
$ao->country_id = config('site')->country_id; // @todo This might be wrong
|
||||||
$ao->language_id = config('SITE')->language_id; // @todo This might be wrong
|
|
||||||
$ao->currency_id = config('SITE')->currency_id; // @todo This might be wrong
|
|
||||||
$ao->active = 1;
|
$ao->active = 1;
|
||||||
$uo->accounts()->save($ao);
|
$uo->accounts()->save($ao);
|
||||||
|
|
||||||
@ -97,28 +98,32 @@ class OrderController extends Controller
|
|||||||
$so = new Service;
|
$so = new Service;
|
||||||
|
|
||||||
// @todo Make this automatic
|
// @todo Make this automatic
|
||||||
$so->site_id = config('SITE')->site_id;
|
$so->site_id = config('site')->site_id;
|
||||||
$so->product_id = $request->input('product_id');
|
$so->product_id = $po->id;
|
||||||
$so->order_status = 'ORDER-SUBMIT';
|
$so->order_status = 'ORDER-SUBMIT';
|
||||||
$so->orderby_id = Auth::id();
|
$so->ordered_by = Auth::id();
|
||||||
$so->model = get_class($options);
|
$so->active = FALSE;
|
||||||
|
$so->model = $order ? get_class($order) : NULL;
|
||||||
|
$so->recur_schedule = $po->billing_interval;
|
||||||
|
|
||||||
if ($options->order_info) {
|
if ($order && $order->order_info) {
|
||||||
$so->order_info = $options->order_info;
|
$so->order_info = $order->order_info;
|
||||||
|
|
||||||
unset($options->order_info);
|
unset($order->order_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
$so = $ao->services()->save($so);
|
$so = $ao->services()->save($so);
|
||||||
|
|
||||||
if ($options instanceOf Model) {
|
if ($order instanceOf Model) {
|
||||||
$options->service_id = $so->id;
|
$order->service_id = $so->id;
|
||||||
$options->save();
|
$order->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @todo Move this email to a config item
|
||||||
Mail::to('help@graytech.net.au')
|
Mail::to('help@graytech.net.au')
|
||||||
->queue((new OrderRequest($so,$request->input('options.notes')))->onQueue('email')); //@todo Get email from DB.
|
->queue((new OrderRequest($so,$request->input('options.notes') ?: ''))->onQueue('email')); //@todo Get email from DB.
|
||||||
|
|
||||||
return view('order_received',['o'=>$so]);
|
return view('theme.backend.adminlte.order_received')
|
||||||
|
->with('o',$so);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -200,6 +200,7 @@ class PaypalController extends Controller
|
|||||||
foreach ($response->result->purchase_units as $pu) {
|
foreach ($response->result->purchase_units as $pu) {
|
||||||
foreach ($pu->payments->captures as $cap) {
|
foreach ($pu->payments->captures as $cap) {
|
||||||
$po = new Payment;
|
$po = new Payment;
|
||||||
|
$po->active = TRUE;
|
||||||
|
|
||||||
switch ($cap->status) {
|
switch ($cap->status) {
|
||||||
case 'PENDING':
|
case 'PENDING':
|
||||||
@ -217,7 +218,7 @@ class PaypalController extends Controller
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$po->date_payment = Carbon::parse($cap->create_time);
|
$po->paid_at = Carbon::parse($cap->create_time);
|
||||||
$po->checkout_id = $this->o->id;
|
$po->checkout_id = $this->o->id;
|
||||||
$po->checkout_data = $cap->id;
|
$po->checkout_data = $cap->id;
|
||||||
|
|
||||||
@ -229,7 +230,7 @@ class PaypalController extends Controller
|
|||||||
$pio = new PaymentItem;
|
$pio = new PaymentItem;
|
||||||
$pio->site_id = 1; // @todo To implement
|
$pio->site_id = 1; // @todo To implement
|
||||||
$pio->invoice_id = $cap->invoice_id;
|
$pio->invoice_id = $cap->invoice_id;
|
||||||
$pio->alloc_amt = $cap->amount->value-$po->fees_amt;
|
$pio->amount = $cap->amount->value-$po->fees_amt;
|
||||||
|
|
||||||
$po->items->push($pio);
|
$po->items->push($pio);
|
||||||
|
|
||||||
|
149
app/Http/Controllers/ProductController.php
Normal file
149
app/Http/Controllers/ProductController.php
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
use App\Http\Requests\ProductAddEdit;
|
||||||
|
use App\Models\{Product,ProductTranslate};
|
||||||
|
|
||||||
|
class ProductController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get a list of products that meet a type
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return Collection
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function api_supplier_products(Request $request): Collection
|
||||||
|
{
|
||||||
|
switch ($request->type) {
|
||||||
|
case Product\Broadband::class:
|
||||||
|
return Product\Broadband::select(['id','supplier_item_id'])
|
||||||
|
->with(['supplied.supplier_detail.supplier'])
|
||||||
|
->get()
|
||||||
|
->map(function($item) { return ['id'=>$item->id,'name'=>sprintf('%s: %s',$item->supplied->supplier->name,$item->supplied->name)]; })
|
||||||
|
->sortBy('name')
|
||||||
|
->values();
|
||||||
|
|
||||||
|
case Product\Domain::class:
|
||||||
|
return Product\Domain::select(['id','supplier_item_id'])
|
||||||
|
->with(['supplied.supplier_detail.supplier'])
|
||||||
|
->get()
|
||||||
|
->map(function($item) { return ['id'=>$item->id,'name'=>sprintf('%s: %s',$item->supplied->supplier->name,$item->supplied->name)]; })
|
||||||
|
->sortBy('name')
|
||||||
|
->values();
|
||||||
|
|
||||||
|
case Product\Email::class:
|
||||||
|
return Product\Email::select(['id','supplier_item_id'])
|
||||||
|
->with(['supplied.supplier_detail.supplier'])
|
||||||
|
->get()
|
||||||
|
->map(function($item) { return ['id'=>$item->id,'name'=>sprintf('%s: %s',$item->supplied->supplier->name,$item->supplied->name)]; })
|
||||||
|
->sortBy('name')
|
||||||
|
->values();
|
||||||
|
|
||||||
|
case Product\Generic::class:
|
||||||
|
return Product\Generic::select(['id','supplier_item_id'])
|
||||||
|
->with(['supplied.supplier_detail.supplier'])
|
||||||
|
->get()
|
||||||
|
->map(function($item) { return ['id'=>$item->id,'name'=>sprintf('%s: %s',$item->supplied->supplier->name,$item->supplied->name)]; })
|
||||||
|
->sortBy('name')
|
||||||
|
->values();
|
||||||
|
|
||||||
|
case Product\Host::class:
|
||||||
|
return Product\Host::select(['id','supplier_item_id'])
|
||||||
|
->with(['supplied.supplier_detail.supplier'])
|
||||||
|
->get()
|
||||||
|
->map(function($item) { return ['id'=>$item->id,'name'=>sprintf('%s: %s',$item->supplied->supplier->name,$item->supplied->name)]; })
|
||||||
|
->sortBy('name')
|
||||||
|
->values();
|
||||||
|
|
||||||
|
case Product\Phone::class:
|
||||||
|
return Product\Phone::select(['id','supplier_item_id'])
|
||||||
|
->with(['supplied.supplier_detail.supplier'])
|
||||||
|
->get()
|
||||||
|
->map(function($item) { return ['id'=>$item->id,'name'=>sprintf('%s: %s',$item->supplied->supplier->name,$item->supplied->name)]; })
|
||||||
|
->sortBy('name')
|
||||||
|
->values();
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \Exception('Unknown type: '.$request->type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a suppliers details
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param Product $o
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function details(Request $request,Product $o)
|
||||||
|
{
|
||||||
|
if (! $o->exists && $request->name)
|
||||||
|
$o = Product::where('name',$request->name)->firstOrNew();
|
||||||
|
|
||||||
|
return view('theme.backend.adminlte.product.details')
|
||||||
|
->with('breadcrumb',collect(['Products'=>url('a/product')]))
|
||||||
|
->with('o',$o);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function details_addedit(ProductAddEdit $request,Product $o)
|
||||||
|
{
|
||||||
|
foreach ($request->except(['_token','submit','translate','accounting']) as $key => $item)
|
||||||
|
$o->{$key} = $item;
|
||||||
|
|
||||||
|
$o->active = (bool)$request->active;
|
||||||
|
|
||||||
|
// Trim down the pricing array, remove null values
|
||||||
|
$o->pricing = $o->pricing->map(function($item) {
|
||||||
|
foreach ($item as $k=>$v) {
|
||||||
|
if (is_array($v)) {
|
||||||
|
$v = array_filter($v);
|
||||||
|
$item[$k] = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $item;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
$o->save();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return redirect()->back()->withErrors($e->getMessage())->withInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
$o->load(['translate']);
|
||||||
|
$oo = $o->translate ?: new ProductTranslate;
|
||||||
|
foreach ($request->get('translate',[]) as $key => $item)
|
||||||
|
$oo->{$key} = $item;
|
||||||
|
|
||||||
|
$o->translate()->save($oo);
|
||||||
|
|
||||||
|
if ($request->accounting)
|
||||||
|
foreach ($request->accounting as $k=>$v)
|
||||||
|
$o->providers()->syncWithoutDetaching([
|
||||||
|
$k => [
|
||||||
|
'ref' => $v,
|
||||||
|
'site_id'=>$o->site_id,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('success','Product saved');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manage products for a site
|
||||||
|
*
|
||||||
|
* @note This method is protected by the routes
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function home()
|
||||||
|
{
|
||||||
|
return view('theme.backend.adminlte.product.home');
|
||||||
|
}
|
||||||
|
}
|
@ -2,27 +2,18 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Auth;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
use App\Models\Account;
|
||||||
|
|
||||||
class ResellerServicesController extends Controller
|
class ResellerServicesController extends Controller
|
||||||
{
|
{
|
||||||
public function accounts()
|
public function services(Request $request,Account $o)
|
||||||
{
|
{
|
||||||
return ['data'=>Auth::user()->all_accounts()->values()];
|
return $o->services
|
||||||
}
|
->filter(function($item) use ($request) {
|
||||||
|
return $item->active || ($item->id == $request->include);
|
||||||
public function agents()
|
});
|
||||||
{
|
|
||||||
return ['data'=>Auth::user()->all_agents()->values()];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function clients()
|
|
||||||
{
|
|
||||||
return ['data'=>Auth::user()->all_clients()->values()];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function service_inactive()
|
|
||||||
{
|
|
||||||
return ['data'=>Auth::user()->all_client_service_inactive()->values()];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,10 +3,11 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
|
||||||
use App\Models\{Account,Invoice,Service,Service\Adsl,User};
|
use App\Models\{Account,Invoice,Payment,Service,Supplier,User};
|
||||||
|
|
||||||
class SearchController extends Controller
|
class SearchController extends Controller
|
||||||
{
|
{
|
||||||
@ -14,73 +15,86 @@ class SearchController extends Controller
|
|||||||
* Search from the Application Dashboard.
|
* Search from the Application Dashboard.
|
||||||
*
|
*
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @return Response
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
public function search(Request $request)
|
public function search(Request $request): Collection
|
||||||
{
|
{
|
||||||
|
$result = collect();
|
||||||
|
|
||||||
|
// If the user isnt logged in
|
||||||
|
if (! Auth::user())
|
||||||
|
abort(401,'Need to login');
|
||||||
|
|
||||||
// If there isnt a term value, return null
|
// If there isnt a term value, return null
|
||||||
if (! $request->input('term'))
|
if (! $request->input('term'))
|
||||||
return [];
|
return $result;
|
||||||
|
|
||||||
$result = collect();
|
$account_ids = ($x=Auth::user()->accounts)->pluck('id');
|
||||||
$accounts = ($x=Auth::user()->all_accounts())->pluck('id');
|
$user_ids = $x->transform(function($item) { return $item->user;})->pluck('id');
|
||||||
$users = $x->transform(function($item) { return $item->user;});
|
|
||||||
|
|
||||||
# Look for User
|
# Look for User
|
||||||
foreach (User::Search($request->input('term'))
|
foreach (User::Search($request->input('term'))
|
||||||
->whereIN('id',$users->pluck('id'))
|
->whereIN('id',$user_ids)
|
||||||
->orderBy('lastname')
|
->orderBy('lastname')
|
||||||
->orderBy('firstname')
|
->orderBy('firstname')
|
||||||
->limit(10)->get() as $o)
|
->limit(10)->get() as $o)
|
||||||
{
|
{
|
||||||
$result->push(['name'=>sprintf('%s %s',$o->aid,$o->name),'value'=>'/u/home/'.$o->id,'category'=>'Users']);
|
$result->push(['name'=>sprintf('%s (%s) - %s',$o->name,$o->lid,$o->email),'value'=>'/u/home/'.$o->id,'category'=>'Users']);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Look for User by Supplier
|
||||||
|
if (is_numeric($request->input('term')))
|
||||||
|
foreach (User::select(['users.*','suppliers.name AS supplier_name','supplier_user.id AS pivot_id'])
|
||||||
|
->join('supplier_user',['supplier_user.user_id'=>'users.id'])
|
||||||
|
->join('suppliers',['suppliers.id'=>'supplier_user.supplier_id'])
|
||||||
|
->whereIN('user_id',$user_ids)
|
||||||
|
->where('supplier_user.id','like','%'.$request->input('term').'%')
|
||||||
|
->orderBy('lastname')
|
||||||
|
->orderBy('firstname')
|
||||||
|
->limit(10)->get() as $o)
|
||||||
|
{
|
||||||
|
$result->push(['name'=>sprintf('%s (%s:%s)',$o->name,$o->supplier_name,$o->pivot_id),'value'=>'/u/home/'.$o->id,'category'=>'Suppliers']);
|
||||||
}
|
}
|
||||||
|
|
||||||
# Look for Account
|
# Look for Account
|
||||||
foreach (Account::Search($request->input('term'))
|
foreach (Account::Search($request->input('term'))
|
||||||
->whereIN('user_id',$users->pluck('id'))
|
->whereIN('user_id',$user_ids)
|
||||||
->orderBy('company')
|
->orderBy('company')
|
||||||
->limit(10)->get() as $o)
|
->limit(10)->get() as $o)
|
||||||
{
|
{
|
||||||
$result->push(['name'=>sprintf('%s %s',$o->aid,$o->company),'value'=>'/u/home/'.$o->user_id,'category'=>'Accounts']);
|
$result->push(['name'=>sprintf('%s (%s)',$o->company,$o->lid),'value'=>'/u/home/'.$o->user_id,'category'=>'Accounts']);
|
||||||
}
|
}
|
||||||
|
|
||||||
# Look for a Service
|
# Look for a Service
|
||||||
foreach (Service::Search($request->input('term'))
|
foreach (Service::Search($request->input('term'))
|
||||||
->whereIN('account_id',$accounts)
|
->whereIN('account_id',$account_ids)
|
||||||
->orderBy('id')
|
->orderBy('id')
|
||||||
->limit(10)->get() as $o)
|
->limit(20)->get() as $o)
|
||||||
{
|
{
|
||||||
$result->push(['name'=>sprintf('%s (%s)',$o->name,$o->sid),'value'=>'/u/service/'.$o->id,'category'=>'Services']);
|
$result->push(['name'=>sprintf('%s (%s) %s',$o->name,$o->lid,$o->active ? '' : '<small>INACT</small>'),'value'=>'/u/service/'.$o->id,'category'=>$o->category_name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
# Look for an Invoice
|
# Look for an Invoice
|
||||||
foreach (Invoice::Search($request->input('term'))
|
foreach (Invoice::Search($request->input('term'))
|
||||||
->whereIN('account_id',$accounts)
|
->whereIN('account_id',$account_ids)
|
||||||
->orderBy('id')
|
->orderBy('id')
|
||||||
->limit(10)->get() as $o)
|
->limit(10)->get() as $o)
|
||||||
{
|
{
|
||||||
$result->push(['name'=>sprintf('%s: %s',$o->sid,$o->account->name),'value'=>'/u/invoice/'.$o->id,'category'=>'Invoices']);
|
$result->push(['name'=>sprintf('%s: %s',$o->lid,$o->account->name),'value'=>'/u/invoice/'.$o->id,'category'=>'Invoices']);
|
||||||
}
|
}
|
||||||
|
|
||||||
# Look for an ADSL/NBN Service
|
if (Gate::any(['wholesaler'],new Payment)) {
|
||||||
foreach (Adsl::Search($request->input('term'))
|
# Look for Payments
|
||||||
->whereIN('account_id',$accounts)
|
foreach (Payment::Search($request->input('term'))
|
||||||
->orderBy('service_number')
|
->whereIN('account_id',$account_ids)
|
||||||
->limit(10)->get() as $o)
|
->limit(10)->get() as $o)
|
||||||
{
|
{
|
||||||
$result->push(['name'=>sprintf('%s (%s)',$o->name,$o->service->sid),'value'=>'/u/service/'.$o->id,'category'=>'Broadband']);
|
$result->push(['name'=>sprintf('%s: %s $%s',$o->lid,$o->account->name,number_format($o->total,2)),'value'=>'/a/payment/addedit/'.$o->id,'category'=>'Payments']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Look for Domain Name
|
return $result
|
||||||
foreach (Service\Domain::Search($request->input('term'))
|
->sortBy(function($item) { return $item['category'].$item['name']; })
|
||||||
->whereIN('account_id',$accounts)
|
->values();
|
||||||
->orderBy('domain_name')
|
|
||||||
->limit(10)->get() as $o)
|
|
||||||
{
|
|
||||||
$result->push(['name'=>sprintf('%s (%s)',$o->service_name,$o->service->sid),'value'=>'/u/service/'.$o->id,'category'=>'Domains']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,146 +2,463 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
use Illuminate\Support\Facades\Session;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||||
|
|
||||||
use App\Models\Service;
|
use App\Http\Requests\ServiceChangeRequest;
|
||||||
|
use App\Mail\{CancelRequest,ChangeRequest};
|
||||||
|
use App\Models\{Charge,Product,Service};
|
||||||
|
|
||||||
class ServiceController extends Controller
|
class ServiceController extends Controller
|
||||||
{
|
{
|
||||||
|
/* SERVICE WORKFLOW METHODS */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edit a domain service details
|
* Cancel a request to cancel a service
|
||||||
|
*
|
||||||
|
* @param Service $o
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function action_cancel_cancel(Service $o): bool
|
||||||
|
{
|
||||||
|
if (! $o->order_info)
|
||||||
|
$o->order_info = collect();
|
||||||
|
|
||||||
|
$o->order_info->put('cancel_cancel',Carbon::now()->format('Y-m-d H:i:s'));
|
||||||
|
$o->order_status = 'ACTIVE';
|
||||||
|
|
||||||
|
return $o->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function action_cancel_pending_enter(Service $o): bool
|
||||||
|
{
|
||||||
|
$o->order_status = 'CANCEL-PENDING';
|
||||||
|
|
||||||
|
return $o->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function action_cancelled(Service $o): bool
|
||||||
|
{
|
||||||
|
$o->order_status = 'CANCELLED';
|
||||||
|
$o->active = FALSE;
|
||||||
|
|
||||||
|
return $o->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel a request to change a service
|
||||||
|
*
|
||||||
|
* @param Service $o
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function action_change_cancel(Service $o): bool
|
||||||
|
{
|
||||||
|
if (! $o->order_info)
|
||||||
|
$o->order_info = collect();
|
||||||
|
|
||||||
|
// @todo add some validation if this doesnt return a result
|
||||||
|
$np = $o->changes()->where('service__change.active',TRUE)->where('complete',FALSE)->get()->pop();
|
||||||
|
$np->pivot->active = FALSE;
|
||||||
|
$np->pivot->save();
|
||||||
|
|
||||||
|
$o->order_status = 'ACTIVE';
|
||||||
|
|
||||||
|
return $o->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action to change a service order_status to another stage
|
||||||
|
* This is a generic function that can redirect the user to a page that is required to completed to enter
|
||||||
|
* the new stage
|
||||||
|
*
|
||||||
|
* @param Service $o
|
||||||
|
* @param string $stage
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
|
*/
|
||||||
|
private function action_request_enter_redirect(Service $o,string $stage)
|
||||||
|
{
|
||||||
|
return redirect(sprintf('u/service/%d/%s',$o->id,strtolower($stage)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OTHER METHODS */
|
||||||
|
|
||||||
|
public function change_pending(ServiceChangeRequest $request,Service $o)
|
||||||
|
{
|
||||||
|
// @todo add some validation if this doesnt return a result
|
||||||
|
$np = $o->changes()->where('service__change.active',TRUE)->where('complete',FALSE)->get()->pop();
|
||||||
|
|
||||||
|
if ($request->post()) {
|
||||||
|
foreach ($this->service_change_charges($request,$o) as $co)
|
||||||
|
$co->save();
|
||||||
|
|
||||||
|
$o->product_id = Arr::get($request->broadband,'product_id');
|
||||||
|
$o->price = Arr::get($request->broadband,'price');
|
||||||
|
$o->order_status = 'ACTIVE';
|
||||||
|
$o->save();
|
||||||
|
|
||||||
|
$np->pivot->complete = TRUE;
|
||||||
|
$np->pivot->effective_at = Carbon::now();
|
||||||
|
$np->pivot->save();
|
||||||
|
|
||||||
|
return redirect()->to(url('u/service',[$o->id]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('theme.backend.adminlte.service.change_pending')
|
||||||
|
->with('breadcrumb',collect()->merge($o->account->breadcrumb))
|
||||||
|
->with('o',$o)
|
||||||
|
->with('np',$np);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a request to cancel a service
|
||||||
*
|
*
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param Service $o
|
* @param Service $o
|
||||||
* @return \Illuminate\Http\RedirectResponse
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
*/
|
*/
|
||||||
public function domain_edit(Request $request,Service $o)
|
public function cancel_request(Request $request,Service $o)
|
||||||
{
|
{
|
||||||
session()->flash('service_update',TRUE);
|
if ($request->post()) {
|
||||||
|
$request->validate([
|
||||||
$validation = $request->validate([
|
'stop_at'=>'required|date',
|
||||||
'service.domain_name' => sprintf('required|unique:%s,domain_name,%d',$o->type->getTable(),$o->type->id),
|
|
||||||
'service.domain_expire' => 'required|date',
|
|
||||||
'service.domain_tld_id' => 'required|exists:ab_domain_tld,id',
|
|
||||||
'service.domain_registrar_id' => 'required|exists:ab_domain_registrar,id',
|
|
||||||
'service.registrar_account' => 'required',
|
|
||||||
'service.registrar_username' => 'required|string|min:5',
|
|
||||||
'service.registrar_ns' => 'required|string|min:5',
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$o->type->forceFill($validation['service'])->save();
|
if (! $o->order_info)
|
||||||
|
$o->order_info = collect();
|
||||||
|
|
||||||
return redirect()->back()->with('success','Record updated.');
|
$o->stop_at = $request->stop_at;
|
||||||
|
$o->order_info->put('cancel_note',$request->notes);
|
||||||
|
$o->order_status = 'CANCEL-REQUEST';
|
||||||
|
$o->save();
|
||||||
|
|
||||||
|
//@todo Get email from DB.
|
||||||
|
Mail::to('help@graytech.net.au')
|
||||||
|
->queue((new CancelRequest($o,$request->notes))->onQueue('email'));
|
||||||
|
|
||||||
|
return redirect('u/service/'.$o->id)->with('success','Cancellation lodged');
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('theme.backend.adminlte.service.cancel_request')
|
||||||
|
->with('o',$o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the status of a service
|
||||||
|
* @todo This needs to be optimized
|
||||||
|
*
|
||||||
|
* @note This route is protected by middleware @see ServicePolicy::progress()
|
||||||
|
* It is assumed that the next stage is valid for the services current stage - validated in ServicePolicy::progress()
|
||||||
|
* @param Service $o
|
||||||
|
* @param string $stage
|
||||||
|
* @return RedirectResponse
|
||||||
|
*/
|
||||||
|
public function change(Service $o,string $stage): RedirectResponse
|
||||||
|
{
|
||||||
|
// While stage has a string value, that indicates the next stage we want to go to
|
||||||
|
// If stage is NULL, the current stage hasnt been completed
|
||||||
|
// If stage is FALSE, then the current stage failed, and may optionally be directed to another stage.
|
||||||
|
|
||||||
|
while ($stage) {
|
||||||
|
// Check that stage is a valid next action for the user currently performing it
|
||||||
|
//$current = $this->getStageParameters($this->order_status);
|
||||||
|
$next = $o->getStageParameters($stage);
|
||||||
|
|
||||||
|
// If valid, call the method to confirm that the current stage is complete
|
||||||
|
if ($x=$next->get('enter_method')) {
|
||||||
|
if (! method_exists($this,$x))
|
||||||
|
abort(500,sprintf('ENTER_METHOD [%s]defined doesnt exist',$x));
|
||||||
|
|
||||||
|
Log::debug(sprintf('Running ENTER_METHOD [%s] on Service [%d] to go to stage [%s]',$x,$o->id,$stage));
|
||||||
|
|
||||||
|
// @todo Should call exit_method of the current stage first, to be sure we can change
|
||||||
|
|
||||||
|
try {
|
||||||
|
$result = $this->{$x}($o,$stage);
|
||||||
|
|
||||||
|
// If we have a form to complete, we need to return with a URL, so we can catch that with an Exception
|
||||||
|
} catch (HttpException $e) {
|
||||||
|
if ($e->getStatusCode() == 301)
|
||||||
|
return ($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Error Condition
|
||||||
|
if (is_null($result))
|
||||||
|
return redirect()->to('u/service/'.$o->id);
|
||||||
|
|
||||||
|
elseif ($result instanceof RedirectResponse)
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
// The service cannot enter the next stage
|
||||||
|
elseif (! $result)
|
||||||
|
abort(500,'Current Method FAILED: '.$result);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$o->order_status = $stage;
|
||||||
|
|
||||||
|
if ($stage == 'ACTIVE')
|
||||||
|
$o->active = TRUE;
|
||||||
|
|
||||||
|
$o->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If valid, call the method to start the next stage
|
||||||
|
$stage = ''; // @todo this is temporary, we havent written the code to automatically jump to the next stage if wecan
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->to('u/service/'.$o->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a request to cancel a service
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param Service $o
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
|
*/
|
||||||
|
public function change_request(Request $request,Service $o)
|
||||||
|
{
|
||||||
|
if ($request->post()) {
|
||||||
|
$request->validate([
|
||||||
|
'product_id'=>'required|exists:products,id',
|
||||||
|
'change_date'=>'required|date',
|
||||||
|
'notes'=>'nullable|min:10',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$o->changes()->attach([$o->id=>[
|
||||||
|
'site_id'=> $o->site_id,
|
||||||
|
'ordered_by' => Auth::id(),
|
||||||
|
'ordered_at' => Carbon::now(),
|
||||||
|
'effective_at' => $request->change_date,
|
||||||
|
'product_id' => $request->product_id,
|
||||||
|
'notes' => $request->notes,
|
||||||
|
'active' => TRUE,
|
||||||
|
'complete' => FALSE,
|
||||||
|
]]);
|
||||||
|
|
||||||
|
$o->order_status = 'CHANGE-REQUEST';
|
||||||
|
$o->save();
|
||||||
|
|
||||||
|
//@todo Get email from DB.
|
||||||
|
Mail::to('help@graytech.net.au')
|
||||||
|
->queue((new ChangeRequest($o,$request->notes))->onQueue('email'));
|
||||||
|
|
||||||
|
return redirect('u/service/'.$o->id)->with('success','Upgrade requested');
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (get_class($o->type)) {
|
||||||
|
default:
|
||||||
|
return view('theme.backend.adminlte.service.change_request')
|
||||||
|
->with('breadcrumb',collect()->merge($o->account->breadcrumb))
|
||||||
|
->with('o',$o);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List all the domains managed by the user
|
* List all the domains managed by the user
|
||||||
*
|
*
|
||||||
* @return View
|
* @return View
|
||||||
|
* @todo revalidate
|
||||||
*/
|
*/
|
||||||
public function domain_list(): View
|
public function domain_list(): View
|
||||||
{
|
{
|
||||||
$o = Service\Domain::serviceActive()
|
$o = Service\Domain::serviceActive()
|
||||||
->serviceUserAuthorised(Auth::user())
|
->serviceUserAuthorised(Auth::user())
|
||||||
->select('service_domains.*')
|
->select('service_domain.*')
|
||||||
->join('ab_service',['ab_service.id'=>'service_domains.service_id'])
|
->join('services',['services.id'=>'service_domain.service_id'])
|
||||||
->with(['service.account','registrar'])
|
->with(['service.account','registrar'])
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
return view('r.service.domain.list')
|
return view('theme.backend.adminlte.service.domain.list')
|
||||||
|
->with('o',$o);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function email_list(): View
|
||||||
|
{
|
||||||
|
// @todo Need to add the with path when calculating next_billed and price
|
||||||
|
$o = Service\Email::serviceActive()
|
||||||
|
->serviceUserAuthorised(Auth::user())
|
||||||
|
->select('service_email.*')
|
||||||
|
->join('services',['services.id'=>'service_email.service_id'])
|
||||||
|
->with(['service.account','service.product.type.supplied.supplier_detail.supplier','tld'])
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return view('theme.backend.adminlte.service.email.list')
|
||||||
->with('o',$o);
|
->with('o',$o);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a service
|
* Return details on the users service
|
||||||
|
*
|
||||||
|
* @param Service $o
|
||||||
|
* @return View
|
||||||
|
*/
|
||||||
|
public function home(Service $o): View
|
||||||
|
{
|
||||||
|
return view('theme.backend.adminlte.service.home')
|
||||||
|
->with('breadcrumb',collect()->merge($o->account->breadcrumb))
|
||||||
|
->with('o',$o);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hosting_list(): View
|
||||||
|
{
|
||||||
|
// @todo Need to add the with path when calculating next_billed and price
|
||||||
|
$o = Service\Host::serviceActive()
|
||||||
|
->serviceUserAuthorised(Auth::user())
|
||||||
|
->select('service_host.*')
|
||||||
|
->join('services',['services.id'=>'service_host.service_id'])
|
||||||
|
->with(['service.account','service.product.type.supplied.supplier_detail.supplier','tld'])
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return view('theme.backend.adminlte.service.host.list')
|
||||||
|
->with('o',$o);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function service_change_charges(Request $request,Service $o): Collection
|
||||||
|
{
|
||||||
|
$charges = collect();
|
||||||
|
$po = Product::findOrFail(Arr::get($request->broadband,'product_id'));
|
||||||
|
$start_at = Carbon::create(Arr::get($request->broadband,'start_at'));
|
||||||
|
|
||||||
|
// Get the invoiced items covering the start_at date
|
||||||
|
foreach ($o->invoice_items->filter(function($item) use ($start_at) {
|
||||||
|
return ($item->start_at < $start_at) && ($item->stop_at > $start_at) && ($item->item_type === 0);
|
||||||
|
}) as $iio)
|
||||||
|
{
|
||||||
|
// Reverse the original charge
|
||||||
|
$co = new Charge;
|
||||||
|
$co->active = TRUE;
|
||||||
|
$co->service_id = $o->id;
|
||||||
|
$co->account_id = $o->account_id;
|
||||||
|
$co->sweep_type = 6;
|
||||||
|
$co->product_id = $iio->product_id;
|
||||||
|
$co->description = 'Plan Upgrade Adjustment';
|
||||||
|
$co->user_id = Auth::id();
|
||||||
|
$co->type = $iio->item_type;
|
||||||
|
$co->start_at = $start_at;
|
||||||
|
$co->stop_at = $iio->stop_at;
|
||||||
|
$co->amount = $iio->price_base;
|
||||||
|
$co->taxable = TRUE; // @todo this should be determined
|
||||||
|
$co->quantity = -1*$start_at->diff($iio->stop_at)->days/$iio->start_at->diff($iio->stop_at)->days;
|
||||||
|
$charges->push($co);
|
||||||
|
|
||||||
|
// Add the new charge
|
||||||
|
$co = new Charge;
|
||||||
|
$co->active = TRUE;
|
||||||
|
$co->service_id = $o->id;
|
||||||
|
$co->account_id = $o->account_id;
|
||||||
|
$co->sweep_type = 6;
|
||||||
|
$co->product_id = $po->id;
|
||||||
|
$co->description = 'Plan Upgrade Adjustment';
|
||||||
|
$co->user_id = Auth::id();
|
||||||
|
$co->type = $iio->item_type;
|
||||||
|
$co->start_at = $start_at;
|
||||||
|
$co->stop_at = $iio->stop_at;
|
||||||
|
$co->amount = Arr::get($request->broadband,'price') ?: $po->base_charge;
|
||||||
|
$co->taxable = TRUE; // @todo this should be determined
|
||||||
|
$co->quantity = $start_at->diff($iio->stop_at)->days/$iio->start_at->diff($iio->stop_at)->days;
|
||||||
|
$charges->push($co);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add any fee
|
||||||
|
if (Arr::get($request->broadband,'change_fee')) {
|
||||||
|
$co = new Charge;
|
||||||
|
$co->active = TRUE;
|
||||||
|
$co->service_id = $o->id;
|
||||||
|
$co->account_id = $o->account_id;
|
||||||
|
$co->sweep_type = 6;
|
||||||
|
$co->product_id = $po->id;
|
||||||
|
$co->description = 'Plan Upgrade Fee';
|
||||||
|
$co->user_id = Auth::id();
|
||||||
|
$co->type = 3;
|
||||||
|
$co->start_at = $start_at;
|
||||||
|
$co->stop_at = $start_at;
|
||||||
|
$co->amount = Arr::get($request->broadband,'change_fee');
|
||||||
|
$co->taxable = TRUE; // @todo this should be determined
|
||||||
|
$co->quantity = 1;
|
||||||
|
$charges->push($co);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $charges;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an API method, that works with service change - to return the new charges as a result of changing a service
|
||||||
*
|
*
|
||||||
* @note: Route Middleware protects this path
|
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param Service $o
|
* @param Service $o
|
||||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function service_change_charges_display(Request $request,Service $o)
|
||||||
|
{
|
||||||
|
return view('theme.backend.adminlte.a.charge.service_change')
|
||||||
|
->with('charges',$this->service_change_charges($request,$o));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update details about a service
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param Service $o
|
||||||
|
* @return RedirectResponse
|
||||||
|
* @throws ValidationException
|
||||||
*/
|
*/
|
||||||
public function update(Request $request,Service $o)
|
public function update(Request $request,Service $o)
|
||||||
{
|
{
|
||||||
switch ($o->order_status) {
|
if ($o->type->validation()) {
|
||||||
case 'CANCEL-REQUEST':
|
Session::put('service_update',true);
|
||||||
if ($request->post()) {
|
$validator = Validator::make($x=$request->post($o->category),$o->type->validation());
|
||||||
if (! $request->post('date_end'))
|
|
||||||
return redirect()->back()->withErrors('Cancellation Date not provided');
|
|
||||||
|
|
||||||
$o->date_end = $request->post('date_end');
|
if ($validator->fails()) {
|
||||||
|
return redirect()
|
||||||
foreach (['cancel_notes'] as $key) {
|
->back()
|
||||||
if ($request->post($key))
|
->withErrors($validator)
|
||||||
$o->setOrderInfo($key,$request->post($key));
|
->withInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
$o->order_status='CANCEL-PENDING';
|
$o->type->forceFill($validator->validated());
|
||||||
$o->save();
|
|
||||||
|
|
||||||
return redirect()->to(url('u/service',$o->id))->with('updated','Service cancellation submitted.');
|
} elseif ($request->post($o->product->category)) {
|
||||||
}
|
$o->type->forceFill($request->post($o->product->category));
|
||||||
|
|
||||||
return $this->update_request_cancel($o);
|
|
||||||
|
|
||||||
case 'ORDER-SENT':
|
|
||||||
if ($request->post()) {
|
|
||||||
foreach (['reference','notes'] as $key) {
|
|
||||||
$o->setOrderInfo($key,$request->post($key));
|
|
||||||
}
|
|
||||||
|
|
||||||
$o->save();
|
|
||||||
|
|
||||||
foreach ($request->post($o->stype) as $k=>$v) {
|
|
||||||
$o->type->{$k} = $v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$o->type->save();
|
$o->type->save();
|
||||||
|
|
||||||
return redirect()->to(url('u/service',$o->id))->with('updated','Order sent notes updated.');
|
if ($request->post('invoice_next_at'))
|
||||||
|
$o->invoice_next_at = $request->invoice_next_at;
|
||||||
|
|
||||||
|
if ($request->post('recur_schedule'))
|
||||||
|
$o->recur_schedule = $request->recur_schedule;
|
||||||
|
|
||||||
|
$o->suspend_billing = ($request->suspend_billing == 'on');
|
||||||
|
$o->external_billing = ($request->external_billing == 'on');
|
||||||
|
$o->price = $request->price ?: NULL;
|
||||||
|
|
||||||
|
// Also update our service start_at date.
|
||||||
|
// @todo We may want to make start_at/stop_at dynamic values calculated by the type records
|
||||||
|
if ($request->post('start_at'))
|
||||||
|
$o->start_at = $request->start_at;
|
||||||
|
else {
|
||||||
|
// For broadband, start_at is connect_at in the type record
|
||||||
|
switch ($o->category) {
|
||||||
|
case 'broadband':
|
||||||
|
$o->start_at = $o->type->connect_at;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->update_order_status($o);
|
|
||||||
|
|
||||||
case 'PROVISION-PLANNED':
|
|
||||||
if ($request->post()) {
|
|
||||||
foreach (['provision_notes'] as $key) {
|
|
||||||
$o->setOrderInfo($key,$request->post($key));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$o->date_start = $request->post('date_start');
|
|
||||||
|
|
||||||
$o->save();
|
$o->save();
|
||||||
|
|
||||||
foreach ($request->post($o->stype) as $k=>$v) {
|
return redirect()->back()->with('success','Record Updated');
|
||||||
$o->type->{$k} = $v;
|
|
||||||
}
|
|
||||||
|
|
||||||
$o->type->save();
|
|
||||||
|
|
||||||
return redirect()->to(url('u/service',$o->id))->with('updated','Order sent notes updated.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->update_provision_planned($o);
|
|
||||||
|
|
||||||
default:
|
|
||||||
abort(499,'Not yet implemented');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function update_order_status(Service $o)
|
|
||||||
{
|
|
||||||
return View('r.service.order.sent',['o'=>$o]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function update_request_cancel(Service $o)
|
|
||||||
{
|
|
||||||
return View('u.service.order.cancel',['o'=>$o]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function update_provision_planned(Service $o)
|
|
||||||
{
|
|
||||||
return View('r.service.order.provision_plan',['o'=>$o]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
223
app/Http/Controllers/SupplierController.php
Normal file
223
app/Http/Controllers/SupplierController.php
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
use App\Http\Requests\{SupplierAddEdit,SupplierProductAddEdit};
|
||||||
|
use App\Models\{Cost,Supplier,SupplierDetail};
|
||||||
|
use App\Jobs\ImportCosts;
|
||||||
|
|
||||||
|
class SupplierController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Update a suppliers details
|
||||||
|
*
|
||||||
|
* @param SupplierAddEdit $request
|
||||||
|
* @param Supplier $o
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function addedit(SupplierAddEdit $request,Supplier $o)
|
||||||
|
{
|
||||||
|
$this->middleware(['auth','wholesaler']);
|
||||||
|
|
||||||
|
foreach ($request->except(['_token','supplier_details','api_key','api_secret','submit']) as $key => $item)
|
||||||
|
$o->{$key} = $item;
|
||||||
|
|
||||||
|
$o->active = (bool)$request->active;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$o->save();
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return redirect()->back()->withErrors($e->getMessage())->withInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
$o->load(['detail']);
|
||||||
|
$oo = $o->detail ?: new SupplierDetail;
|
||||||
|
|
||||||
|
foreach ($request->get('supplier_details',[]) as $key => $item)
|
||||||
|
$oo->{$key} = $item;
|
||||||
|
|
||||||
|
$oo->connections = $oo->connections->merge([
|
||||||
|
'api_key'=>$request->get('api_key'),
|
||||||
|
'api_secret'=>$request->get('api_secret'),
|
||||||
|
])->filter();
|
||||||
|
$o->detail()->save($oo);
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('success','Supplier Saved');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Site up site wide suppliers, or a site's supplier details
|
||||||
|
*
|
||||||
|
* @note This method is protected by the routes
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function admin_home()
|
||||||
|
{
|
||||||
|
$this->middleware(['auth','wholesaler']);
|
||||||
|
|
||||||
|
return view('theme.backend.adminlte.supplier.home');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the suppliers invoice
|
||||||
|
*
|
||||||
|
* @param Cost $o
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function cost(Cost $o)
|
||||||
|
{
|
||||||
|
// @todo Need to add the services that are active that are not on the bill for the supplier.
|
||||||
|
return view('theme.backend.adminlte.supplier.cost.view')
|
||||||
|
->with('o',$o);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cost_add(Supplier $o)
|
||||||
|
{
|
||||||
|
return view('theme.backend.adminlte.supplier.cost.add')
|
||||||
|
->with('o',$o);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cost_submit(Request $request,Supplier $o)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'file' => 'required|filled',
|
||||||
|
'billed_at' => 'required|date',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$filename = $request->file('file')->store('cost_import');
|
||||||
|
|
||||||
|
ImportCosts::dispatch(
|
||||||
|
config('site'),
|
||||||
|
$o,
|
||||||
|
Carbon::create($request->billed_at),
|
||||||
|
$filename,
|
||||||
|
)->onQueue('low');
|
||||||
|
|
||||||
|
return redirect()->back()->with('success','File uploaded');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New Product from a supplier
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function product_add()
|
||||||
|
{
|
||||||
|
return view('theme.backend.adminlte.supplier.product.addedit')
|
||||||
|
->with('o',new Supplier)
|
||||||
|
->with('oo',NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function product_addedit(SupplierProductAddEdit $request,Supplier $o,int $id,string $type)
|
||||||
|
{
|
||||||
|
// Quick validation
|
||||||
|
if ($type !== $request->offering_type)
|
||||||
|
abort(500,'Type and offering type do not match');
|
||||||
|
if ($o->exists && ($o->detail->id !== (int)$request->supplier_detail_id))
|
||||||
|
abort(500,sprintf('Supplier [%d] and supplier_detail_id [%d] do not match',$o->detail->id,$request->supplier_detail_id));
|
||||||
|
|
||||||
|
switch ($request->offering_type) {
|
||||||
|
case 'broadband':
|
||||||
|
$oo = Supplier\Broadband::findOrNew($id);
|
||||||
|
|
||||||
|
// @todo these are broadband requirements - get them from the broadband class.
|
||||||
|
foreach ($request->only([
|
||||||
|
'supplier_detail_id',
|
||||||
|
'product_id'.
|
||||||
|
'product_desc',
|
||||||
|
'base_cost',
|
||||||
|
'setup_cost',
|
||||||
|
'contract_term',
|
||||||
|
'metric',
|
||||||
|
'speed',
|
||||||
|
'technology',
|
||||||
|
'offpeak_start',
|
||||||
|
'offpeak_end',
|
||||||
|
'base_down_peak',
|
||||||
|
'base_up_peak',
|
||||||
|
'base_down_offpeak',
|
||||||
|
'base_up_offpeak',
|
||||||
|
'extra_down_peak',
|
||||||
|
'extra_up_peak',
|
||||||
|
'extra_down_offpeak',
|
||||||
|
'extra_up_offpeak',
|
||||||
|
]) as $key => $value)
|
||||||
|
$oo->$key = $value;
|
||||||
|
|
||||||
|
// Our boolean values
|
||||||
|
foreach ($request->only(['active','extra_shaped','extra_charged']) as $key => $value)
|
||||||
|
$oo->$key = ($value == 'on' ? 1 : 0);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \Exception('Unknown offering type:'.$request->offering_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
$oo->save();
|
||||||
|
|
||||||
|
return redirect()->back()
|
||||||
|
->with('success','Saved');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit a supplier product
|
||||||
|
*
|
||||||
|
* @param Supplier $o
|
||||||
|
* @param int $id
|
||||||
|
* @param string $type
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function product_view(Supplier $o,int $id,string $type)
|
||||||
|
{
|
||||||
|
$oo = $o->detail->find($type,$id);
|
||||||
|
$oo->load(['products.product.services.product.type']);
|
||||||
|
|
||||||
|
return view('theme.backend.adminlte.supplier.product.addedit')
|
||||||
|
->with('o',$o)
|
||||||
|
->with('oo',$oo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the form for a specific product type
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param string $type
|
||||||
|
* @param int|null $id
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function product_view_type(Request $request,string $type,int $id=NULL)
|
||||||
|
{
|
||||||
|
$o = $id ? Supplier::offeringTypeClass($type)->findOrFail($id) : NULL;
|
||||||
|
|
||||||
|
if ($request->old)
|
||||||
|
$request->session()->flashInput($request->old);
|
||||||
|
|
||||||
|
if ($o)
|
||||||
|
$o->load(['products.product.services']);
|
||||||
|
|
||||||
|
return view('theme.backend.adminlte.supplier.product.widget.'.$type)
|
||||||
|
->with('o',$id ? $o : NULL)
|
||||||
|
->withErrors($request->errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View a supplier.
|
||||||
|
*
|
||||||
|
* @param Supplier|null $o
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function view(?Supplier $o)
|
||||||
|
{
|
||||||
|
$this->middleware(['auth','wholesaler']);
|
||||||
|
|
||||||
|
return view('theme.backend.adminlte.supplier.details')
|
||||||
|
->with('o',$o);
|
||||||
|
}
|
||||||
|
}
|
@ -1,102 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use App\Models\Supplier;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
|
|
||||||
class SuppliersController extends Controller
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->middleware('auth ');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display a listing of the resource.
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function index()
|
|
||||||
{
|
|
||||||
return view('r/supplier/index');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the form for creating a new resource.
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function create()
|
|
||||||
{
|
|
||||||
return view('r/supplier/create');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a newly created resource in storage.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function store(Request $request)
|
|
||||||
{
|
|
||||||
// @todo Insure site index is included.
|
|
||||||
$o = new Supplier;
|
|
||||||
$o->name = $request->input('name');
|
|
||||||
$o->active = 1;
|
|
||||||
$o->account_mgr = '';
|
|
||||||
$o->account_email = '';
|
|
||||||
$o->email_provision = '';
|
|
||||||
$o->email_support = '';
|
|
||||||
$o->phone_provision = '';
|
|
||||||
$o->phone_support = '';
|
|
||||||
$o->save();
|
|
||||||
|
|
||||||
echo 'REDIRECT TO <a href="'.url('r/supplier/index').'">here</a>';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the specified resource.
|
|
||||||
*
|
|
||||||
* @param \App\suppliers $suppliers
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function show(suppliers $suppliers)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the form for editing the specified resource.
|
|
||||||
*
|
|
||||||
* @param \App\suppliers $suppliers
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function edit(suppliers $suppliers)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the specified resource in storage.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @param \App\suppliers $suppliers
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function update(Request $request, suppliers $suppliers)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the specified resource from storage.
|
|
||||||
*
|
|
||||||
* @param \App\suppliers $suppliers
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function destroy(suppliers $suppliers)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,13 +24,13 @@ class AccountController extends Controller
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Get our invoice due date for this invoice
|
// Get our invoice due date for this invoice
|
||||||
$io->due_date = $s->min(function($item) { return $item->invoice_next; });
|
$io->due_at = $s->min(function($item) { return $item->invoice_next; });
|
||||||
|
|
||||||
// @todo The days in advance is an application parameter
|
// @todo The days in advance is an application parameter
|
||||||
$io->date_orig = $io->due_date->subDays(30);
|
$io->created_at = $io->due_at->subDays(30);
|
||||||
|
|
||||||
// Work out items to add to this invoice, plus any in the next additional days
|
// Work out items to add to this invoice, plus any in the next additional days
|
||||||
$days = now()->diffInDays($io->due_date)+1+7;
|
$days = now()->diffInDays($io->due_at)+1+7;
|
||||||
foreach ($s as $so)
|
foreach ($s as $so)
|
||||||
{
|
{
|
||||||
if ($so->isInvoiceDueSoon($days))
|
if ($so->isInvoiceDueSoon($days))
|
||||||
@ -38,6 +38,7 @@ class AccountController extends Controller
|
|||||||
$io->items->push($o);
|
$io->items->push($o);
|
||||||
}
|
}
|
||||||
|
|
||||||
return View('u.invoice.home',['o'=>$io]);
|
return view('theme.backend.adminlte.u.invoice.home')
|
||||||
|
->with('o',$io);
|
||||||
}
|
}
|
||||||
}
|
}
|
60
app/Http/Controllers/UserController.php
Normal file
60
app/Http/Controllers/UserController.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Session;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
use App\Models\{Supplier,User};
|
||||||
|
|
||||||
|
class UserController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Add a supplier to a user's profile
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param User $o
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function supplier_addedit(Request $request,User $o)
|
||||||
|
{
|
||||||
|
Session::put('supplier_update',true);
|
||||||
|
|
||||||
|
$validated = $request->validate([
|
||||||
|
'id'=> ['required','string',Rule::unique('supplier_user')->where(fn ($query) => $query->where('supplier_id',$request->supplier_id)->where('user_id','<>',$o->id))],
|
||||||
|
'supplier_id'=>'required|exists:suppliers,id',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$o->suppliers()->attach([
|
||||||
|
$validated['supplier_id'] => [
|
||||||
|
'id'=>$validated['id'],
|
||||||
|
'site_id'=>$o->site_id,
|
||||||
|
'created_at'=>Carbon::now(),
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('success','Supplier Added');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a supplier from a user's profile
|
||||||
|
*
|
||||||
|
* @param User $o
|
||||||
|
* @param Supplier $so
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function supplier_delete(User $o,Supplier $so)
|
||||||
|
{
|
||||||
|
Session::put('supplier_update',true);
|
||||||
|
|
||||||
|
$o->suppliers()->detach([$so->id]);
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('success','Supplier Deleted');
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
class WelcomeController extends Controller
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->middleware('demoMode');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function home() {
|
|
||||||
return view('welcome.home');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function under_construction() {
|
|
||||||
abort(499,'Under Construction');
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,8 +6,18 @@ use App\Http\Controllers\Controller;
|
|||||||
|
|
||||||
class ReportController extends Controller
|
class ReportController extends Controller
|
||||||
{
|
{
|
||||||
|
public function accounts()
|
||||||
|
{
|
||||||
|
return view('account/report');
|
||||||
|
}
|
||||||
|
|
||||||
public function products()
|
public function products()
|
||||||
{
|
{
|
||||||
return view('a/product/report');
|
return view('product/report');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function services()
|
||||||
|
{
|
||||||
|
return view('service/report');
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,86 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
|
||||||
|
|
||||||
class Kernel extends HttpKernel
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The application's global HTTP middleware stack.
|
|
||||||
*
|
|
||||||
* These middleware are run during every request to your application.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $middleware = [
|
|
||||||
\App\Http\Middleware\CheckForMaintenanceMode::class,
|
|
||||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
|
||||||
\App\Http\Middleware\TrimStrings::class,
|
|
||||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
|
||||||
\App\Http\Middleware\TrustProxies::class,
|
|
||||||
\App\Http\Middleware\SetSite::class,
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The application's route middleware groups.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $middlewareGroups = [
|
|
||||||
'web' => [
|
|
||||||
\App\Http\Middleware\EncryptCookies::class,
|
|
||||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
|
||||||
\Illuminate\Session\Middleware\StartSession::class,
|
|
||||||
// \Illuminate\Session\Middleware\AuthenticateSession::class,
|
|
||||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
|
||||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
|
||||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
|
||||||
// \App\Http\Middleware\SetSite::class,
|
|
||||||
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
|
|
||||||
],
|
|
||||||
|
|
||||||
'api' => [
|
|
||||||
'throttle:60,1',
|
|
||||||
'bindings',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The application's route middleware.
|
|
||||||
*
|
|
||||||
* These middleware may be assigned to groups or used individually.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $routeMiddleware = [
|
|
||||||
'auth' => \App\Http\Middleware\Authenticate::class,
|
|
||||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
|
||||||
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
|
|
||||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
|
||||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
|
||||||
'demoMode' => \Spatie\DemoMode\DemoMode::class,
|
|
||||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
|
||||||
'role' => \App\Http\Middleware\Role::class,
|
|
||||||
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
|
|
||||||
'theme' => \Igaster\LaravelTheme\Middleware\setTheme::class,
|
|
||||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
|
||||||
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The priority-sorted list of middleware.
|
|
||||||
*
|
|
||||||
* This forces non-global middleware to always be in the given order.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $middlewarePriority = [
|
|
||||||
\Illuminate\Session\Middleware\StartSession::class,
|
|
||||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
|
||||||
\App\Http\Middleware\Authenticate::class,
|
|
||||||
\Illuminate\Session\Middleware\AuthenticateSession::class,
|
|
||||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
|
||||||
\Illuminate\Auth\Middleware\Authorize::class,
|
|
||||||
];
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
|
||||||
|
|
||||||
use Illuminate\Auth\Middleware\Authenticate as Middleware;
|
|
||||||
|
|
||||||
class Authenticate extends Middleware
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Get the path the user should be redirected to when they are not authenticated.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function redirectTo($request)
|
|
||||||
{
|
|
||||||
if (! $request->expectsJson()) {
|
|
||||||
return route('login');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode as Middleware;
|
|
||||||
|
|
||||||
class CheckForMaintenanceMode extends Middleware
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The URIs that should be reachable while maintenance mode is enabled.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $except = [
|
|
||||||
//
|
|
||||||
];
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
|
||||||
|
|
||||||
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
|
|
||||||
|
|
||||||
class EncryptCookies extends Middleware
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The names of the cookies that should not be encrypted.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $except = [
|
|
||||||
'toggleState',
|
|
||||||
];
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
|
|
||||||
class RedirectIfAuthenticated
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Handle an incoming request.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @param \Closure $next
|
|
||||||
* @param string|null $guard
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function handle($request, Closure $next, $guard = null)
|
|
||||||
{
|
|
||||||
if (Auth::guard($guard)->check()) {
|
|
||||||
return redirect('/home');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,15 +2,19 @@
|
|||||||
|
|
||||||
namespace App\Http\Middleware;
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Closure;
|
use Closure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable us to use the role during middleware authorisation
|
||||||
|
*/
|
||||||
class Role
|
class Role
|
||||||
{
|
{
|
||||||
public function handle($request, Closure $next, $role)
|
public function handle(Request $request, Closure $next, string $role)
|
||||||
{
|
{
|
||||||
if ($role AND ! Auth::user())
|
if ($role AND ! Auth::user())
|
||||||
return abort(303,'Not Authenticated');
|
abort(403,'Not Authenticated');
|
||||||
|
|
||||||
switch ($role) {
|
switch ($role) {
|
||||||
case 'wholesaler':
|
case 'wholesaler':
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Middleware;
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
use Illuminate\Support\Facades\Config;
|
use Illuminate\Support\Facades\Config;
|
||||||
use Illuminate\Support\Facades\View;
|
use Illuminate\Support\Facades\View;
|
||||||
@ -18,13 +19,11 @@ use App\Models\Site;
|
|||||||
class SetSite
|
class SetSite
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Handle an incoming request.
|
* @param Request $request
|
||||||
*
|
* @param Closure $next
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @param \Closure $next
|
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function handle($request, Closure $next)
|
public function handle(Request $request,Closure $next)
|
||||||
{
|
{
|
||||||
$so = new Site;
|
$so = new Site;
|
||||||
|
|
||||||
@ -43,7 +42,7 @@ class SetSite
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set who we are in SETUP.
|
// Set who we are in SETUP.
|
||||||
Config::set('SITE',$so);
|
Config::set('site',$so);
|
||||||
if (! $request->ajax())
|
if (! $request->ajax())
|
||||||
View::share('site',$so);
|
View::share('site',$so);
|
||||||
|
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
|
|
||||||
|
|
||||||
class TrimStrings extends Middleware
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The names of the attributes that should not be trimmed.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $except = [
|
|
||||||
'password',
|
|
||||||
'password_confirmation',
|
|
||||||
];
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Fideloper\Proxy\TrustProxies as Middleware;
|
|
||||||
|
|
||||||
class TrustProxies extends Middleware
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The trusted proxies for this application.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $proxies;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The headers that should be used to detect proxies.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $headers = Request::HEADER_X_FORWARDED_ALL;
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
|
|
||||||
|
|
||||||
class VerifyCsrfToken extends Middleware
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Indicates whether the XSRF-TOKEN cookie should be set on the response.
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
protected $addHttpCookie = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The URIs that should be excluded from CSRF verification.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $except = [
|
|
||||||
//
|
|
||||||
];
|
|
||||||
}
|
|
36
app/Http/Requests/CheckoutAddEdit.php
Normal file
36
app/Http/Requests/CheckoutAddEdit.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Editing Suppliers
|
||||||
|
*/
|
||||||
|
class CheckoutAddEdit extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return Auth::user()->isWholesaler();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'required|string|min:2|max:255',
|
||||||
|
'active' => 'sometimes|accepted',
|
||||||
|
'description' => 'nullable|string|min:2|max:255',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
41
app/Http/Requests/ProductAddEdit.php
Normal file
41
app/Http/Requests/ProductAddEdit.php
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Editing Suppliers
|
||||||
|
*/
|
||||||
|
class ProductAddEdit extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return Auth::user()->isWholesaler();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'translate.name_short' => 'required|string|min:2|max:100',
|
||||||
|
'translate.name_detail' => 'required|string|min:2|max:100',
|
||||||
|
'translate.description' => 'required|string|min:2|max:65535',
|
||||||
|
'active' => 'sometimes|accepted',
|
||||||
|
'model' => 'sometimes|string', // @todo Check that it is a valid model type
|
||||||
|
'model_id' => 'sometimes|int', // @todo Check that it is a valid model type
|
||||||
|
'accounting' => 'nullable|array', // @todo Validate that the value is in the accounting system
|
||||||
|
'pricing' => 'required|array', // @todo Validate the elements in the pricing
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
39
app/Http/Requests/ServiceChangeRequest.php
Normal file
39
app/Http/Requests/ServiceChangeRequest.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class ServiceChangeRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return $this->route('o')->serviceUserAuthorised(Auth::user());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
* @todo This is specific to broadband - this needs to be more generic.
|
||||||
|
*/
|
||||||
|
public function rules(Request $request)
|
||||||
|
{
|
||||||
|
if (! $request->isMethod('post'))
|
||||||
|
return [];
|
||||||
|
|
||||||
|
return [
|
||||||
|
'broadband.product_id' => 'required|exists:products,id',
|
||||||
|
'broadband.change_fee' => 'nullable|numeric',
|
||||||
|
'broadband.price' => 'nullable|numeric',
|
||||||
|
'broadband.start_at' => 'required|date', // @todo Check that it is not more than 1 billing cycle ago, and not future.
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
46
app/Http/Requests/SupplierAddEdit.php
Normal file
46
app/Http/Requests/SupplierAddEdit.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Editing Suppliers
|
||||||
|
*/
|
||||||
|
class SupplierAddEdit extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return Auth::user()->isWholesaler();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'required|string|min:2|max:255',
|
||||||
|
'active' => 'sometimes|accepted',
|
||||||
|
'address1' => 'nullable|string|min:2|max:255',
|
||||||
|
'address2' => 'nullable|string|min:2|max:255',
|
||||||
|
'city' => 'nullable|string|min:2|max:64',
|
||||||
|
'state' => 'nullable|string|min:2|max:32',
|
||||||
|
'postcode' => 'nullable|string|min:2|max:8',
|
||||||
|
'supplier_details.notes' => 'nullable|string|min:3',
|
||||||
|
'supplier_details.accounts' => 'nullable|email',
|
||||||
|
'supplier_details.support' => 'nullable|email',
|
||||||
|
'supplier_details.payments' => 'nullable|string|min:3',
|
||||||
|
'api_key' => 'nullable|string|min:3',
|
||||||
|
'api_secret' => 'nullable|string|min:3',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
60
app/Http/Requests/SupplierProductAddEdit.php
Normal file
60
app/Http/Requests/SupplierProductAddEdit.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
use App\Models\Supplier;
|
||||||
|
|
||||||
|
class SupplierProductAddEdit extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return Auth::user()->isWholesaler();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function rules(Request $request)
|
||||||
|
{
|
||||||
|
// @todo these are broadband requirements - perhaps move them to the broadband class.
|
||||||
|
// @todo Enhance the validation so that extra_* values are not accepted if base_* values are not included.
|
||||||
|
return [
|
||||||
|
'id' => 'required|nullable',
|
||||||
|
'offering_type' => ['required',Rule::in(Supplier::offeringTypeKeys()->toArray())],
|
||||||
|
'supplier_detail_id' => 'required|exists:supplier_details,id',
|
||||||
|
'active' => 'sometimes|accepted',
|
||||||
|
'extra_shaped' => 'sometimes|accepted',
|
||||||
|
'extra_charged' => 'sometimes|accepted',
|
||||||
|
'product_id' => 'required|string|min:2',
|
||||||
|
'product_desc' => 'required|string|min:2',
|
||||||
|
'base_cost' => 'required|numeric|min:.01',
|
||||||
|
'setup_cost' => 'nullable|numeric',
|
||||||
|
'contract_term' => 'nullable|numeric|min:1',
|
||||||
|
'metric' => 'nullable|numeric|min:1',
|
||||||
|
'speed' => 'nullable|string|max:64',
|
||||||
|
'technology' => 'nullable|string|max:255',
|
||||||
|
'offpeak_start' => 'nullable|date_format:H:i',
|
||||||
|
'offpeak_end' => 'nullable|date_format:H:i',
|
||||||
|
'base_down_peak' => 'nullable|numeric',
|
||||||
|
'base_up_peak' => 'nullable|numeric',
|
||||||
|
'base_down_offpeak' => 'nullable|numeric',
|
||||||
|
'base_up_offpeak' => 'nullable|numeric',
|
||||||
|
'extra_down_peak' => 'nullable|numeric',
|
||||||
|
'extra_up_peak' => 'nullable|numeric',
|
||||||
|
'extra_down_offpeak' => 'nullable|numeric',
|
||||||
|
'extra_up_offpeak' => 'nullable|numeric',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -9,12 +9,12 @@ interface IDs
|
|||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function getLIDattribute(): string;
|
public function getLIDAttribute(): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the system ID of the item
|
* Return the system ID of the item
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function getSIDattribute(): string;
|
public function getSIDAttribute(): string;
|
||||||
}
|
}
|
29
app/Interfaces/ProductItem.php
Normal file
29
app/Interfaces/ProductItem.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Interfaces;
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
interface ProductItem
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Return the traffic inclusion with the service
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function allowance(): Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the traffic inclusion as a string
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function allowance_string(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this offering capture usage information
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasUsage(): bool;
|
||||||
|
}
|
@ -1,39 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Interfaces;
|
|
||||||
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
|
|
||||||
interface ProductSupplier
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Return the traffic inclusion with the service
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function allowance(): Collection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the traffic inclusion as a string
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function allowance_string(): string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the product cost
|
|
||||||
*
|
|
||||||
* @return float
|
|
||||||
*/
|
|
||||||
public function getCostAttribute(): float;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the supplier class
|
|
||||||
* If there is a model relationship return:
|
|
||||||
* return $this->getRelationValue('supplier');
|
|
||||||
* otherwise return a stdClass with name
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getSupplierAttribute();
|
|
||||||
}
|
|
@ -6,6 +6,13 @@ use Carbon\Carbon;
|
|||||||
|
|
||||||
interface ServiceItem
|
interface ServiceItem
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Months the service is contracted for.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getContractTermAttribute(): int;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the Service Description.
|
* Return the Service Description.
|
||||||
*
|
*
|
||||||
@ -16,7 +23,7 @@ interface ServiceItem
|
|||||||
/**
|
/**
|
||||||
* Date the service expires
|
* Date the service expires
|
||||||
*/
|
*/
|
||||||
public function getServiceExpireAttribute(): Carbon;
|
public function getServiceExpireAttribute(): ?Carbon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the Service Name.
|
* Return the Service Name.
|
||||||
@ -25,6 +32,13 @@ interface ServiceItem
|
|||||||
*/
|
*/
|
||||||
public function getServiceNameAttribute(): string;
|
public function getServiceNameAttribute(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Has this service expired
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasExpired(): bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is this service in a contract
|
* Is this service in a contract
|
||||||
*
|
*
|
||||||
|
@ -6,6 +6,13 @@ use Illuminate\Support\Collection;
|
|||||||
|
|
||||||
interface ServiceUsage
|
interface ServiceUsage
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Our model that holds traffic information
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function traffic();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This service provides usage information
|
* This service provides usage information
|
||||||
*
|
*
|
||||||
|
107
app/Jobs/AccountingAccountSync.php
Normal file
107
app/Jobs/AccountingAccountSync.php
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Intuit\Jobs\AccountingCustomerUpdate;
|
||||||
|
|
||||||
|
use App\Models\{Account,ProviderToken};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronise customers with our accounts.
|
||||||
|
*
|
||||||
|
* This will:
|
||||||
|
* + Create the account in the accounting system
|
||||||
|
* + Update the account in the accounting system with our data (we are master)
|
||||||
|
*/
|
||||||
|
class AccountingAccountSync implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
private const LOGKEY = 'JAS';
|
||||||
|
|
||||||
|
private ProviderToken $to;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @param ProviderToken $to
|
||||||
|
*/
|
||||||
|
public function __construct(ProviderToken $to)
|
||||||
|
{
|
||||||
|
$this->to = $to;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$api = $this->to->API();
|
||||||
|
$ref = Account::select('id','site_id','company','user_id')->with(['user'])->get();
|
||||||
|
|
||||||
|
foreach ($api->getCustomers() as $acc) {
|
||||||
|
$o = NULL;
|
||||||
|
|
||||||
|
// See if we are already linked
|
||||||
|
if (($x=$this->to->provider->accounts->where('pivot.ref',$acc->id))->count() === 1) {
|
||||||
|
$o = $x->pop();
|
||||||
|
|
||||||
|
// If not, see if our reference matches
|
||||||
|
} elseif (($x=$ref->filter(function($item) use ($acc) { return $item->sid == $acc->ref; }))->count() === 1) {
|
||||||
|
$o = $x->pop();
|
||||||
|
|
||||||
|
// Look based on Name
|
||||||
|
} elseif (($x=$ref->filter(function($item) use ($acc) { return $item->company == $acc->companyname || $item->name == $acc->fullname || $item->user->email == $acc->email; }))->count() === 1) {
|
||||||
|
$o = $x->pop();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Log not found
|
||||||
|
Log::alert(sprintf('%s:Customer not found [%s:%s]',self::LOGKEY,$acc->id,$acc->DisplayName));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$o->providers()->syncWithoutDetaching([
|
||||||
|
$this->to->provider->id => [
|
||||||
|
'ref' => $acc->id,
|
||||||
|
'synctoken' => $acc->synctoken,
|
||||||
|
'created_at'=>Carbon::create($acc->created_at),
|
||||||
|
'updated_at'=>Carbon::create($acc->updated_at),
|
||||||
|
'site_id'=>$this->to->site_id,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
Log::alert(sprintf('%s:Customer updated [%s:%s]',self::LOGKEY,$o->id,$acc->id));
|
||||||
|
|
||||||
|
// Check if QB is out of Sync and update it.
|
||||||
|
$acc->syncOriginal();
|
||||||
|
$acc->PrimaryEmailAddr = (object)['Address'=>$o->user->email];
|
||||||
|
$acc->ResaleNum = $o->sid;
|
||||||
|
$acc->GivenName = $o->user->firstname;
|
||||||
|
$acc->FamilyName = $o->user->lastname;
|
||||||
|
$acc->CompanyName = $o->name;
|
||||||
|
$acc->DisplayName = $o->name;
|
||||||
|
$acc->FullyQualifiedName = $o->name;
|
||||||
|
//$acc->Active = (bool)$o->active; // @todo implement in-activity, but only if all invoices are paid and services cancelled
|
||||||
|
|
||||||
|
if ($acc->getDirty()) {
|
||||||
|
Log::info(sprintf('%s:Customer [%s] (%s:%s) has changed',self::LOGKEY,$o->sid,$acc->id,$acc->DisplayName),['dirty'=>$acc->getDirty()]);
|
||||||
|
$acc->sparse = 'true';
|
||||||
|
|
||||||
|
AccountingCustomerUpdate::dispatch($this->to,$acc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo Identify accounts in our DB that are not in accounting
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
142
app/Jobs/AccountingPaymentSync.php
Normal file
142
app/Jobs/AccountingPaymentSync.php
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Models\{Account,Invoice,Payment,PaymentItem,ProviderToken,Site};
|
||||||
|
use Intuit\Models\Payment as PaymentModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronise payments with our payments.
|
||||||
|
*
|
||||||
|
* This will:
|
||||||
|
* + Create/Update the payment in our system
|
||||||
|
* + Associate the payment to the same invoice in our system
|
||||||
|
*/
|
||||||
|
class AccountingPaymentSync implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
private const LOGKEY = 'JPS';
|
||||||
|
|
||||||
|
private PaymentModel $pmi;
|
||||||
|
private ProviderToken $to;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(ProviderToken $to,PaymentModel $pmi)
|
||||||
|
{
|
||||||
|
$this->pmi = $pmi;
|
||||||
|
$this->to = $to;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
// @todo Can this be automatically determined?
|
||||||
|
$site = Site::findOrFail($this->to->site_id);
|
||||||
|
Config::set('site',$site);
|
||||||
|
|
||||||
|
// See if we are already linked
|
||||||
|
if (($x=$this->to->provider->payments->where('pivot.ref',$this->pmi->id))->count() === 1) {
|
||||||
|
$o = $x->pop();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Find the account
|
||||||
|
$ao = Account::select('accounts.*')
|
||||||
|
->join('account__provider',['account__provider.account_id'=>'accounts.id'])
|
||||||
|
->where('provider_oauth_id',$this->to->provider_oauth_id)
|
||||||
|
->where('ref',$this->pmi->account_ref)
|
||||||
|
->single();
|
||||||
|
|
||||||
|
if (! $ao) {
|
||||||
|
Log::alert(sprintf('%s:Account not found for payment [%s:%d]',self::LOGKEY,$this->pmi->id,$this->pmi->account_ref));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the payment
|
||||||
|
$o = new Payment;
|
||||||
|
$o->account_id = $ao->id;
|
||||||
|
$o->site_id = $ao->site_id; // @todo Automatically determine
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the payment details
|
||||||
|
$o->paid_at = $this->pmi->date_paid;
|
||||||
|
$o->active = TRUE;
|
||||||
|
$o->checkout_id = 2; // @todo
|
||||||
|
$o->total_amt = $this->pmi->total_amt;
|
||||||
|
$o->notes = 'Imported from Intuit';
|
||||||
|
$o->save();
|
||||||
|
|
||||||
|
Log::info(sprintf('%s:Recording payment [%s:%3.2f]',self::LOGKEY,$this->pmi->id,$this->pmi->total_amt));
|
||||||
|
|
||||||
|
$o->providers()->syncWithoutDetaching([
|
||||||
|
$this->to->provider->id => [
|
||||||
|
'ref' => $this->pmi->id,
|
||||||
|
'synctoken' => $this->pmi->synctoken,
|
||||||
|
'created_at'=>Carbon::create($this->pmi->created_at),
|
||||||
|
'updated_at'=>Carbon::create($this->pmi->updated_at),
|
||||||
|
'site_id'=>$this->to->site_id,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Load the invoice that this payment pays
|
||||||
|
$invoices = collect();
|
||||||
|
foreach ($this->pmi->lines() as $item => $amount) {
|
||||||
|
$invoice = Invoice::select('invoices.*')
|
||||||
|
->join('invoice__provider',['invoice__provider.invoice_id'=>'invoices.id'])
|
||||||
|
->where('provider_oauth_id',$this->to->provider_oauth_id)
|
||||||
|
->where('ref',$item)
|
||||||
|
->single();
|
||||||
|
|
||||||
|
$invoices->put($item,$invoice);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete existing paid invoices that are no longer paid
|
||||||
|
foreach ($o->items as $pio)
|
||||||
|
if ($invoices->pluck('id')->search($pio->invoice_id) === FALSE)
|
||||||
|
$pio->delete();
|
||||||
|
|
||||||
|
// Update payment items
|
||||||
|
foreach ($this->pmi->lines() as $item => $amount) {
|
||||||
|
if (! $invoices->has($item)) {
|
||||||
|
Log::alert(sprintf('%s:Invoice [%s] not recorded, payment not assigned',self::LOGKEY,$item));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$io = $invoices->get($item);
|
||||||
|
|
||||||
|
// If the payment item already exists
|
||||||
|
if (($x=$o->items->where('invoice_id',$io->id))->count()) {
|
||||||
|
$pio = $x->pop();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$pio = new PaymentItem;
|
||||||
|
$pio->invoice_id = $io->id;
|
||||||
|
$pio->site_id = $io->site_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pio->amount = $amount;
|
||||||
|
$o->items()->save($pio);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::alert(sprintf('%s:Payment updated [%s:%s]',self::LOGKEY,$o->id,$this->pmi->id));
|
||||||
|
}
|
||||||
|
}
|
86
app/Jobs/AccountingTaxSync.php
Normal file
86
app/Jobs/AccountingTaxSync.php
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Models\{Tax,ProviderToken};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronise TAX ids with our taxes.
|
||||||
|
*
|
||||||
|
* This will only update our records, it wont create new records in the account system, nor in our DB
|
||||||
|
*/
|
||||||
|
class AccountingTaxSync implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
private const LOGKEY = 'JTS';
|
||||||
|
|
||||||
|
private ProviderToken $to;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @param ProviderToken $to
|
||||||
|
*/
|
||||||
|
public function __construct(ProviderToken $to)
|
||||||
|
{
|
||||||
|
$this->to = $to;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$api = $this->to->API();
|
||||||
|
$ref = Tax::select(['id','description'])->get();
|
||||||
|
|
||||||
|
foreach ($api->getTaxCodes() as $acc) {
|
||||||
|
$o = NULL;
|
||||||
|
|
||||||
|
// See if we are already linked
|
||||||
|
if (($x=$this->to->provider->taxes->where('pivot.ref',$acc->id))->count() === 1) {
|
||||||
|
$o = $x->pop();
|
||||||
|
|
||||||
|
/*
|
||||||
|
// If not, see if our reference matches
|
||||||
|
} elseif (($x=$ref->filter(function($item) use ($acc) { return $item->sid == $acc->ref; }))->count() === 1) {
|
||||||
|
$o = $x->pop();
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Look based on Name
|
||||||
|
} elseif (($x=$ref->filter(function($item) use ($acc) { return $item->description === $acc->name; }))->count() === 1) {
|
||||||
|
$o = $x->pop();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Log not found
|
||||||
|
Log::alert(sprintf('%s:Tax not found [%s:%s]',self::LOGKEY,$acc->id,$acc->name));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$o->providers()->syncWithoutDetaching([
|
||||||
|
$this->to->provider->id => [
|
||||||
|
'ref' => $acc->id,
|
||||||
|
'synctoken' => $acc->synctoken,
|
||||||
|
'created_at'=>Carbon::create($acc->created_at),
|
||||||
|
'updated_at'=>Carbon::create($acc->updated_at),
|
||||||
|
'site_id'=>$this->to->site_id,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
Log::alert(sprintf('%s:Tax updated [%s:%s]',self::LOGKEY,$o->id,$acc->id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,37 +5,41 @@ namespace App\Jobs;
|
|||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
use App\Mail\TrafficMismatch;
|
|
||||||
use App\Models\Service\Adsl;
|
|
||||||
use App\Models\Service\AdslTraffic;
|
|
||||||
use App\Models\AdslSupplier;
|
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
|
||||||
|
use App\Classes\External\Supplier as ExternalSupplier;
|
||||||
|
use App\Mail\TrafficMismatch;
|
||||||
|
use App\Models\Supplier;
|
||||||
|
use App\Models\Service\Broadband as ServiceBroadband;
|
||||||
|
use App\Models\Usage\Broadband as UsageBroadband;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class BroadbandTraffic
|
* Class BroadbandTraffic
|
||||||
* Read and update the traffic for an Broadband supplier
|
* Read and update the traffic for an Broadband supplier
|
||||||
*
|
*
|
||||||
* @package App\Jobs
|
* @package App\Jobs
|
||||||
*/
|
*/
|
||||||
class BroadbandTraffic implements ShouldQueue
|
final class BroadbandTraffic implements ShouldQueue
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
private const LOGKEY = 'JBT';
|
private const LOGKEY = 'JBT';
|
||||||
|
|
||||||
protected $aso = NULL; // The supplier we are updating from
|
protected int $sid; // The supplier we are updating from
|
||||||
private $class_prefix = 'App\Classes\External\Supplier\\';
|
private const class_prefix = 'App\Classes\External\Supplier\\';
|
||||||
|
|
||||||
public function __construct(AdslSupplier $o)
|
private const traffic = 'broadband';
|
||||||
|
|
||||||
|
public function __construct(int $sid)
|
||||||
{
|
{
|
||||||
$this->aso = $o;
|
$this->sid = $sid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,35 +47,41 @@ class BroadbandTraffic implements ShouldQueue
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
* @todo The column stats_lastupdate is actually the "next" date that stats should be retrieved. Rename it.
|
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
Log::info(sprintf('%s:Importing Broadband Traffic from [%s]',self::LOGKEY,$this->aso->name),['m'=>__METHOD__]);
|
$so = Supplier::findOrFail($this->sid);
|
||||||
|
Log::info(sprintf('%s:Importing Broadband Traffic from [%s]',self::LOGKEY,$so->name));
|
||||||
|
|
||||||
|
if ((! $connection=$so->detail->connections->get('broadband')) || (count(array_intersect(array_keys($connection),ExternalSupplier::traffic_connection_keys)) !== 3))
|
||||||
|
throw new \Exception('No or missing connection details for:'.self::traffic);
|
||||||
|
|
||||||
$u = 0;
|
$u = 0;
|
||||||
|
|
||||||
// Load our class for this supplier
|
// Load our class for this supplier
|
||||||
$class = $this->class_prefix.$this->aso->name;
|
$class = self::class_prefix.$so->name;
|
||||||
if (class_exists($class)) {
|
if (class_exists($class)) {
|
||||||
$o = new $class($this->aso);
|
$o = new $class($so);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Log::error(sprintf('%s: Class doesnt exist: %d',get_class($this),$class));
|
Log::error(sprintf('%s: Class doesnt exist: %s',self::LOGKEY,$class));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$last_update = Carbon::create(Arr::get($connection,'last'))->addDay();
|
||||||
|
Arr::set($connection,'last',$last_update->format('Y-m-d'));
|
||||||
|
|
||||||
// Repeat pull traffic data until yesterday
|
// Repeat pull traffic data until yesterday
|
||||||
while ($this->aso->stats_lastupdate < Carbon::now()->subDay()) {
|
while ($last_update < Carbon::now()->subDay()) {
|
||||||
Log::notice(sprintf('%s:Next update is [%s]',self::LOGKEY,$this->aso->stats_lastupdate->format('Y-m-d')),['m'=>__METHOD__]);
|
Log::notice(sprintf('%s:Next update is [%s]',self::LOGKEY,$last_update->format('Y-m-d')));
|
||||||
|
|
||||||
// Delete traffic, since we'll refresh it.
|
// Delete traffic, since we'll refresh it.
|
||||||
AdslTraffic::where('supplier_id',$this->aso->id)
|
UsageBroadband::where('supplier_id',$so->id)
|
||||||
->where('date',$this->aso->stats_lastupdate)
|
->where('date',$last_update->format('Y-m-d'))
|
||||||
->delete();
|
->delete();
|
||||||
|
|
||||||
$c = 0;
|
$c = 0;
|
||||||
foreach ($o->fetch() as $line) {
|
foreach ($o->fetch($connection,self::traffic) as $line) {
|
||||||
// The first row is our header
|
// The first row is our header
|
||||||
if (! $c++) {
|
if (! $c++) {
|
||||||
$fields = $o->getColumns(preg_replace('/,\s+/',',',$line),collect($o->header()));
|
$fields = $o->getColumns(preg_replace('/,\s+/',',',$line),collect($o->header()));
|
||||||
@ -79,29 +89,26 @@ class BroadbandTraffic implements ShouldQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (! $fields->count())
|
if (! $fields->count())
|
||||||
abort(500,'? No fields in data exportupda');
|
abort(500,'? No fields in data export');
|
||||||
|
|
||||||
$row = str_getcsv(trim($line));
|
$row = str_getcsv(trim($line));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// @todo Put the date format in the DB.
|
|
||||||
$date = Carbon::createFromFormat('Y-m-d',$row[$o->getColumnKey('Date')]);
|
$date = Carbon::createFromFormat('Y-m-d',$row[$o->getColumnKey('Date')]);
|
||||||
|
// Find the right service dependent on the dates we supplied the service
|
||||||
// Find the right service dependant on the dates we supplied the service
|
$oo = ServiceBroadband::where('service_username',$row[$o->getColumnKey('Login')])
|
||||||
$oo = Adsl::where('service_username',$row[$o->getColumnKey('Login')])
|
->select(DB::raw('service_broadband.*'))
|
||||||
->select(DB::raw('ab_service__adsl.*'))
|
->join('services','services.id','=','service_id')
|
||||||
->join('ab_service','ab_service.id','=','service_id')
|
->where('services.start_at','<=',$date)
|
||||||
->where('ab_service.date_start','<=',$date->format('U'))
|
|
||||||
->where(function($query) use ($date) {
|
->where(function($query) use ($date) {
|
||||||
$query->whereNULL('ab_service.date_end')
|
$query->whereNULL('services.stop_at')
|
||||||
->orWhere('ab_service.date_end','<=',$date->format('U'));
|
->orWhere('services.stop_at','<=',$date);
|
||||||
})
|
})
|
||||||
->get();
|
->single();
|
||||||
|
|
||||||
$to = new AdslTraffic;
|
$to = new UsageBroadband;
|
||||||
$to->site_id = 1; // @todo TO ADDRESS
|
$to->date = $last_update;
|
||||||
$to->date = $this->aso->stats_lastupdate;
|
$to->supplier_id = $so->id;
|
||||||
$to->supplier_id = $this->aso->id;
|
|
||||||
$to->up_peak = $row[$o->getColumnKey('Peak upload')];
|
$to->up_peak = $row[$o->getColumnKey('Peak upload')];
|
||||||
$to->up_offpeak = $row[$o->getColumnKey('Off peak upload')];
|
$to->up_offpeak = $row[$o->getColumnKey('Off peak upload')];
|
||||||
$to->down_peak = $row[$o->getColumnKey('Peak download')];
|
$to->down_peak = $row[$o->getColumnKey('Peak download')];
|
||||||
@ -111,33 +118,39 @@ class BroadbandTraffic implements ShouldQueue
|
|||||||
$to->time = '24:00'; // @todo
|
$to->time = '24:00'; // @todo
|
||||||
|
|
||||||
// If we have no records
|
// If we have no records
|
||||||
if ($oo->count() != 1) {
|
if (! $oo) {
|
||||||
Log::error(sprintf('%s:Too many services return for [%s]',self::LOGKEY,$row[$o->getColumnKey('Login')]),['m'=>__METHOD__,'date'=>$date,'count'=>$oo->count()]);
|
Log::error(sprintf('%s:None or too many services return for [%s]',self::LOGKEY,$row[$o->getColumnKey('Login')]),['date'=>$date]);
|
||||||
|
|
||||||
$to->service = $row[$o->getColumnKey('Login')];
|
$to->service = $row[$o->getColumnKey('Login')];
|
||||||
$to->save();
|
$to->site_id = 1; // @todo This needs to be worked out a better way
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$oo->first()->traffic()->save($to);
|
$to->site_id = $oo->site_id;
|
||||||
|
$to->service_item_id = $oo->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($to->save())
|
||||||
$u++;
|
$u++;
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
Log::error(sprintf('%s:Exception occurred when storing traffic record for [%s].',self::LOGKEY,$row[$o->getColumnKey('Login')]),['m'=>__METHOD__,'row'=>$row,'line'=>$line]);
|
Log::error(sprintf('%s:Exception occurred when storing traffic record for [%s].',self::LOGKEY,$row[$o->getColumnKey('Login')]),['row'=>$row,'line'=>$line]);
|
||||||
throw new \Exception('Error while storing traffic date');
|
throw new \Exception('Error while storing traffic data: '.$e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Log::info(sprintf('%s: Records Imported [%d] for [%s]',self::LOGKEY,$u,$last_update->format('Y-m-d')));
|
||||||
|
|
||||||
Log::info(sprintf('%s: Records Imported [%d] for [%s]',self::LOGKEY,$u,$this->aso->stats_lastupdate->format('Y-m-d')),['m'=>__METHOD__]);
|
// Save our current progress.
|
||||||
|
$so->detail->connections = $so->detail->connections->put(self::traffic,array_merge($connection,['last'=>$last_update->format('Y-m-d')]));
|
||||||
|
$so->detail->save();
|
||||||
|
|
||||||
|
// Update our details for the next iteration.
|
||||||
|
$last_update = $last_update->addDay();
|
||||||
|
Arr::set($connection,'last',$last_update->format('Y-m-d'));
|
||||||
|
|
||||||
if ($u) {
|
if ($u) {
|
||||||
$this->aso->stats_lastupdate = $this->aso->stats_lastupdate->addDay();
|
if ($so->trafficMismatch($date)->count())
|
||||||
$this->aso->save();
|
|
||||||
|
|
||||||
if ($this->aso->trafficMismatch($date)->count())
|
|
||||||
Mail::to('deon@graytech.net.au') // @todo To change
|
Mail::to('deon@graytech.net.au') // @todo To change
|
||||||
->send(new TrafficMismatch($this->aso,$date));
|
->send(new TrafficMismatch($so,$date));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
232
app/Jobs/ImportCosts.php
Normal file
232
app/Jobs/ImportCosts.php
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Models\{Cost,Service,Site,Supplier};
|
||||||
|
use App\Traits\Import;
|
||||||
|
|
||||||
|
class ImportCosts implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable,InteractsWithQueue,Queueable,SerializesModels,Import;
|
||||||
|
|
||||||
|
private const LOGKEY = 'JIC';
|
||||||
|
private Cost $co;
|
||||||
|
private Site $site;
|
||||||
|
private string $file;
|
||||||
|
protected Collection $columns;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Site $site,Supplier $so,Carbon $invoice_date,string $file)
|
||||||
|
{
|
||||||
|
$this->file = $file;
|
||||||
|
$this->site = $site;
|
||||||
|
|
||||||
|
$this->co = Cost::where('site_id',$site->site_id)
|
||||||
|
->where('supplier_id',$so->id)
|
||||||
|
->where('billed_at',$invoice_date)
|
||||||
|
->firstOrNew();
|
||||||
|
$this->co->active = TRUE;
|
||||||
|
$this->co->site_id = $site->site_id;
|
||||||
|
$this->co->billed_at = $invoice_date;
|
||||||
|
$this->co->supplier_id = $so->id;
|
||||||
|
$this->co->save();
|
||||||
|
|
||||||
|
Cost\Broadband::where('cost_id',$this->co->id)->where('site_id',$site->site_id)->delete();
|
||||||
|
Cost\Phone::where('cost_id',$this->co->id)->where('site_id',$site->site_id)->delete();
|
||||||
|
Cost\Generic::where('cost_id',$this->co->id)->where('site_id',$site->site_id)->delete();
|
||||||
|
|
||||||
|
// @todo to be stored in supplier config
|
||||||
|
$headers = [
|
||||||
|
'INVOICEID'=>'Item ID',
|
||||||
|
'REF'=>'Reference No',
|
||||||
|
'IDTAG'=>'ID Tag',
|
||||||
|
'CATEGORY'=>'Category',
|
||||||
|
'DESC'=>'Item Description',
|
||||||
|
'QUANTITY'=>'Quantity',
|
||||||
|
'PRICEUNIT'=>'Unit Price (inc-GST)',
|
||||||
|
'PRICETOTAL'=>'Total (inc-GST)'
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->columns = collect($headers)->transform(function($item) { return strtolower($item); });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
Config::set('site',$this->site);
|
||||||
|
$skip = 7; // @todo to be stored in supplier config
|
||||||
|
|
||||||
|
$file = fopen('storage/app/'.$this->file,'r');
|
||||||
|
$haveHeader = FALSE;
|
||||||
|
|
||||||
|
$c = 0;
|
||||||
|
while (! feof($file)) {
|
||||||
|
$line = stream_get_line($file,0,"\r\n");
|
||||||
|
if (str_starts_with($line,'#'))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Remove any embedded CR and BOM
|
||||||
|
$line = str_replace("\r",'',$line);
|
||||||
|
$line = preg_replace('/^\x{feff}/u','',$line);
|
||||||
|
if (($c++ < $skip) || (! $line))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// The first line is a header.
|
||||||
|
if (! $haveHeader) {
|
||||||
|
Log::debug(sprintf('%s: Input File: %s',get_class($this),$this->file));
|
||||||
|
Log::debug(sprintf('%s: Processing columns: %s',get_class($this),join('|',$this->setColumns($line)->toArray())));
|
||||||
|
$haveHeader = TRUE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the line has a , between two (), then convert the comma to a space.
|
||||||
|
$x = [];
|
||||||
|
if (preg_match('#\(.+,.+\)#i',$line,$x)) {
|
||||||
|
$replace = str_replace(',','_',$x[0]);
|
||||||
|
$line = str_replace($x[0],$replace,$line);
|
||||||
|
//dd($line,$x);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = str_getcsv(trim($line));
|
||||||
|
if (is_null($x=$this->getColumnKey('DESC')) OR empty($fields[$x]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// The first part of our item description is the service number.
|
||||||
|
// This should go to a "supplier" function, since all suppliers may show different values in description.
|
||||||
|
$m = [];
|
||||||
|
$desc = $fields[$x];
|
||||||
|
// m[1] = Service, m[2] = Desc, m[3] = From Date, m[4] = To Date
|
||||||
|
preg_match('#^([0-9]{10})\s+-\s+(.*)\(([0-9]+\s+[JFMASOND].*\s+[0-9]+)+\s+-\s+([0-9]+\s+[JFMASOND].*\s+[0-9]+)+\)$#',$fields[$x],$m);
|
||||||
|
|
||||||
|
if (count($m) !== 5) {
|
||||||
|
dump(sprintf('ERROR: Description didnt parse [%s] on line [%d]',$fields[$x],$c));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cost = ($x=$this->getColumnKey('PRICETOTAL')) ? str_replace([',','$'],'',$fields[$x]) : NULL;
|
||||||
|
$start_at = Carbon::createFromFormat('d M Y',$m[3]);
|
||||||
|
$stop_at = Carbon::createFromFormat('d M Y',$m[4]);
|
||||||
|
$so = Service::search($m[1])->active()->with(['type','product.type.supplied'])->single();
|
||||||
|
|
||||||
|
if ($so) {
|
||||||
|
// r[1] = Monthly Charge or Extra Charge,r[2] = "On Plan", r[3] = Plan Info
|
||||||
|
$r = [];
|
||||||
|
switch ($so->category) {
|
||||||
|
case 'broadband':
|
||||||
|
$to = Cost\Broadband::where('site_id',$this->co->site_id)
|
||||||
|
->where('cost_id',$this->co->id)
|
||||||
|
->where('service_broadband_id',$so->type->id)
|
||||||
|
->where('start_at',$start_at)
|
||||||
|
->where('end_at',$stop_at)
|
||||||
|
->firstOrNew();
|
||||||
|
$to->service_broadband_id = $so->type->id;
|
||||||
|
|
||||||
|
preg_match('#^(Monthly Internet Charge|Plan Change Fee|Change billing date refund for Monthly Internet Charge On Plan|First 12 Month VISP broadband plan discount|.*)\s?(On Plan)?\s?(.*)#',$m[2],$r);
|
||||||
|
|
||||||
|
switch ($r[1]) {
|
||||||
|
case 'Monthly Internet Charge':
|
||||||
|
case 'First 12 Month VISP broadband plan discount':
|
||||||
|
case 'Change billing date refund for Monthly Internet Charge On Plan':
|
||||||
|
$to->base =+ $cost;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Plan Change Fee':
|
||||||
|
$to->excess =+ $cost;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dump(['extra charge'=>$r]);
|
||||||
|
$to->excess =+ $cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'phone':
|
||||||
|
$to = Cost\Phone::where('site_id',$this->co->site_id)
|
||||||
|
->where('cost_id',$this->co->id)
|
||||||
|
->where('service_phone_id',$so->type->id)
|
||||||
|
->where('start_at',$start_at)
|
||||||
|
->where('end_at',$stop_at)
|
||||||
|
->firstOrNew();
|
||||||
|
$to->service_phone_id = $so->type->id;
|
||||||
|
|
||||||
|
preg_match('#^(Residential VOIP Plan Excess Usage|Virtual FAX Number Monthly Rental|Corporate VOIP Plan Monthly Rental|Residential VOIP Plan Monthly Rental|.*)\s?(.*)#',$m[2],$r);
|
||||||
|
|
||||||
|
switch ($r[1]) {
|
||||||
|
case 'Residential VOIP Plan Monthly Rental':
|
||||||
|
case 'Virtual FAX Number Monthly Rental':
|
||||||
|
case 'Corporate VOIP Plan Monthly Rental':
|
||||||
|
$to->base =+ $cost;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Residential VOIP Plan Excess Usage':
|
||||||
|
$to->excess =+ $cost;
|
||||||
|
$to->notes = $r[2];
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dump(['extra charge'=>$r]);
|
||||||
|
$to->excess =+ $cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dump(['so'=>$so,'category'=>$so->category,'line'=>$line,'m'=>$m,'r'=>$r]);
|
||||||
|
throw new \Exception(sprintf('ERROR: Service type not handled for service [%s] (%s) on line [%d]',$m[1],$so->category,$c));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
dump(['line'=>$line,'sql'=>Service::search($m[1])->active()->with(['type','product.type.supplied'])->toSql()]);
|
||||||
|
|
||||||
|
$to = Cost\Generic::where('site_id',$this->co->site_id)
|
||||||
|
->where('cost_id',$this->co->id)
|
||||||
|
->where('notes',sprintf('%s:%s',$m[1],$m[2]))
|
||||||
|
->where('start_at',$start_at)
|
||||||
|
->where('end_at',$stop_at)
|
||||||
|
->firstOrNew();
|
||||||
|
|
||||||
|
$to->excess =+ $cost;
|
||||||
|
$to->notes = $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
$to->site_id = $this->co->site_id;
|
||||||
|
$to->cost_id = $this->co->id;
|
||||||
|
$to->active = TRUE;
|
||||||
|
$to->start_at = $start_at;
|
||||||
|
$to->end_at = $stop_at;
|
||||||
|
|
||||||
|
// Work out supplier product number
|
||||||
|
Log::warning(sprintf('%s:Supplier product ID not matched',self::LOGKEY),['r'=>$r]);
|
||||||
|
|
||||||
|
//dd($m[2],$cost,$so->product->type->supplied);
|
||||||
|
|
||||||
|
// Work out if this base charge, or extra charge
|
||||||
|
|
||||||
|
//dd(['M'=>__METHOD__,'fields'=>$fields,'DESC'=>$this->getColumnKey('DESC'),'desc'=>$desc,'m'=>$m,'sql'=>$so->toSql(),'bindings'=>$so->getBindings(),'so'=>$so]);
|
||||||
|
$to->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($file);
|
||||||
|
}
|
||||||
|
}
|
104
app/Jobs/PaymentsImport.php
Normal file
104
app/Jobs/PaymentsImport.php
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\External\Payments;
|
||||||
|
use App\Models\{Account,Checkout,Payment};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import payments from payment providers
|
||||||
|
*
|
||||||
|
* @package App\Jobs
|
||||||
|
*/
|
||||||
|
final class PaymentsImport implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
private const LOGKEY = 'JPI';
|
||||||
|
|
||||||
|
protected Payments $o; // The payment provider we are updating from
|
||||||
|
private $class_prefix = 'App\Classes\External\Payments\\';
|
||||||
|
|
||||||
|
public function __construct(Payments $o)
|
||||||
|
{
|
||||||
|
$this->o = $o;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
Log::info(sprintf('%s:Importing Payment Date from [%s]',self::LOGKEY,get_class($this->o)));
|
||||||
|
|
||||||
|
// Get our checkout IDs for this plugin
|
||||||
|
$cos = Checkout::where('plugin',config('services.ezypay.plugin'))->pluck('id');
|
||||||
|
|
||||||
|
foreach ($this->o->getCustomers() as $c) {
|
||||||
|
if ($c->BillingStatus == 'Inactive') {
|
||||||
|
Log::debug(sprintf('%s:Ignoring INACTIVE: [%s] %s %s',self::LOGKEY,$c->EzypayReferenceNumber,$c->Firstname,$c->Surname));
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load Account Details from ReferenceId
|
||||||
|
$ao = Account::where('site_id',(int)substr($c->ReferenceId,0,2))
|
||||||
|
->where('id',(int)substr($c->ReferenceId,2,4))
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (! $ao) {
|
||||||
|
Log::error(sprintf('%s:Missing: [%s] %s %s (%s)',self::LOGKEY,$c->EzypayReferenceNumber,$c->Firstname,$c->Surname,$c->ReferenceId));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the last payment logged
|
||||||
|
$last = Carbon::create(Payment::whereIN('checkout_id',$cos)->where('account_id',$ao->id)->max('paid_at'));
|
||||||
|
|
||||||
|
$o = $this->o->getDebits([
|
||||||
|
'customerId'=>$c->Id,
|
||||||
|
'dateFrom'=>$last->format('Y-m-d'),
|
||||||
|
'dateTo'=>$last->addQuarter()->format('Y-m-d'),
|
||||||
|
'pageSize'=>100,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Log::info(sprintf('%s:Loaded [%d] payments for account: [%s]',self::LOGKEY,$o->count(),$ao->id));
|
||||||
|
|
||||||
|
// Load the payments
|
||||||
|
if ($o->count()) {
|
||||||
|
foreach ($o->reverse() as $p) {
|
||||||
|
$pd = Carbon::createFromTimeString($p->Date);
|
||||||
|
|
||||||
|
// If not success, ignore it.
|
||||||
|
if ($p->Status != 'Success') {
|
||||||
|
Log::alert(sprintf('%s:Payment not successful: [%s] %s %s (%s) [%s]',self::LOGKEY,$pd->format('Y-m-d'),$ao->id,$p->Id,$p->Amount,$p->Status));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$lp = $ao->payments->last();
|
||||||
|
|
||||||
|
if ($lp AND (($pd == $lp->paid_at) OR ($p->Id == $lp->checkout_data))) {
|
||||||
|
Log::alert(sprintf('%s:Payment Already Recorded: [%s] %s %s (%s)',self::LOGKEY,$pd->format('Y-m-d'),$ao->id,$p->Id,$p->Amount));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// New Payment
|
||||||
|
$po = new Payment;
|
||||||
|
$po->active = TRUE;
|
||||||
|
$po->site_id = 1; // @todo
|
||||||
|
$po->paid_at = $pd;
|
||||||
|
$po->checkout_id = '999'; // @todo
|
||||||
|
$po->checkout_data = $p->Id;
|
||||||
|
$po->total_amt = $p->Amount;
|
||||||
|
$ao->payments()->save($po);
|
||||||
|
|
||||||
|
Log::info(sprintf('%s:Recorded: Payment for [%s] %s %s (%s) on %s',self::LOGKEY,$c->EzypayReferenceNumber,$c->Firstname,$c->Surname,$po->id,$pd));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
app/Jobs/ProviderTokenRefresh.php
Normal file
39
app/Jobs/ProviderTokenRefresh.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
use App\Models\ProviderToken;
|
||||||
|
|
||||||
|
class ProviderTokenRefresh implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
private ProviderToken $to;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(ProviderToken $to)
|
||||||
|
{
|
||||||
|
$this->to = $to;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$this->to->refreshToken();
|
||||||
|
}
|
||||||
|
}
|
81
app/Jobs/SupplierDomainSync.php
Normal file
81
app/Jobs/SupplierDomainSync.php
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Models\{Site,Supplier,TLD};
|
||||||
|
|
||||||
|
class SupplierDomainSync implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
private const LOGKEY = 'JSD';
|
||||||
|
|
||||||
|
protected Site $site;
|
||||||
|
protected Supplier $supplier;
|
||||||
|
protected bool $forceprod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Site $site,Supplier $supplier,bool $forceprod=FALSE)
|
||||||
|
{
|
||||||
|
$this->site = $site;
|
||||||
|
$this->supplier = $supplier;
|
||||||
|
$this->forceprod = $forceprod;
|
||||||
|
|
||||||
|
Config::set('site',$site);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$registrar_id = ($x=$this->supplier->registrar()) ? $x->id : NULL;
|
||||||
|
|
||||||
|
foreach ($this->supplier->API($this->forceprod)->getDomains(['fetchall'=>true]) as $domain) {
|
||||||
|
// @todo See if we can find this domain by its ID
|
||||||
|
|
||||||
|
// Find this domain by it's name
|
||||||
|
if (! $to=TLD::domaintld($domain->domain_name)) {
|
||||||
|
Log::alert(sprintf('%s:Domain [%s] from (%s) is not in a TLD that we manage',self::LOGKEY,$this->supplier->name,$domain->domain_name));
|
||||||
|
|
||||||
|
} elseif (($domainpart=strtolower($to->domain_part($domain->domain_name))) && (($x=$to->domains->where('domain_name',$domainpart))->count() === 1)) {
|
||||||
|
$o = $x->pop();
|
||||||
|
$o->registrar_auth_password = $domain->auth_key;
|
||||||
|
$o->expire_at = Carbon::create($domain->expiry_date);
|
||||||
|
$o->registrar_account = $domain->account;
|
||||||
|
$o->registrar_username = '';
|
||||||
|
$o->registrar_ns = Supplier\Domain::nameserver_name($domain->nameservers());
|
||||||
|
if ($registrar_id)
|
||||||
|
$o->domain_registrar_id = $registrar_id;
|
||||||
|
|
||||||
|
if ($o->getDirty()) {
|
||||||
|
Log::info(sprintf('%s:Updating Domain [%s] from (%s)',self::LOGKEY,$domain->domain_name,$this->supplier->name));
|
||||||
|
$o->save();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::info(sprintf('%s:No Change to Domain [%s] from (%s)',self::LOGKEY,$domain->domain_name,$this->supplier->name));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alert an unmanaged name.
|
||||||
|
} else {
|
||||||
|
Log::alert(sprintf('%s:Domain [%s] from (%s) is not one managed in OSB',self::LOGKEY,$this->supplier->name,$domain->domain_name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
app/Listeners/LogSentMessage.php
Normal file
31
app/Listeners/LogSentMessage.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Listeners;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Mail\Events\MessageSent;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class LogSentMessage
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'LSM';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the event.
|
||||||
|
*
|
||||||
|
* @param MessageSent $event
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle(MessageSent $event)
|
||||||
|
{
|
||||||
|
Log::debug(
|
||||||
|
sprintf('%s:Email to [%s] with subject [%s] sent [%s]',
|
||||||
|
self::LOGKEY,
|
||||||
|
collect($event->data['message']->getTo())->transform(function($item) { return $item->getAddress(); })->join(','),
|
||||||
|
$event->data['message']->getSubject(),
|
||||||
|
$event->sent->getMessageId(),
|
||||||
|
),
|
||||||
|
['debug'=>$event->sent->getDebug()]);
|
||||||
|
}
|
||||||
|
}
|
40
app/Listeners/ProviderPaymentCreated.php
Normal file
40
app/Listeners/ProviderPaymentCreated.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Listeners;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Events\ProviderPaymentCreated as Event;
|
||||||
|
use App\Jobs\AccountingPaymentSync as Job;
|
||||||
|
use App\Models\{ProviderOauth,Site,User};
|
||||||
|
|
||||||
|
class ProviderPaymentCreated
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'LPC';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the event.
|
||||||
|
*
|
||||||
|
* @param Event $event
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle(Event $event)
|
||||||
|
{
|
||||||
|
$site = Site::findOrFail(1); // @todo This shouldnt be hard coded
|
||||||
|
Config::set('site',$site);
|
||||||
|
|
||||||
|
$uo = User::findOrFail(1); // @todo This shouldnt be hard coded
|
||||||
|
|
||||||
|
$so = ProviderOauth::where('name',$event->provider)->singleOrFail();
|
||||||
|
if (! ($to=$so->token($uo)))
|
||||||
|
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
||||||
|
|
||||||
|
$api = $to->API();
|
||||||
|
$acc = $api->getPayment($event->paymentData['id']);
|
||||||
|
|
||||||
|
Job::dispatch($to,$acc);
|
||||||
|
}
|
||||||
|
}
|
59
app/Mail/CancelRequest.php
Normal file
59
app/Mail/CancelRequest.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Mail;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Mail\Mailable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
|
use App\Models\Service;
|
||||||
|
|
||||||
|
class CancelRequest extends Mailable
|
||||||
|
{
|
||||||
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public Service $service;
|
||||||
|
public string $notes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new message instance.
|
||||||
|
*
|
||||||
|
* @param Service $o
|
||||||
|
* @param string $notes
|
||||||
|
*/
|
||||||
|
public function __construct(Service $o,string $notes='')
|
||||||
|
{
|
||||||
|
$this->service = $o;
|
||||||
|
$this->notes = $notes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the message.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function build()
|
||||||
|
{
|
||||||
|
Config::set('site',$this->service->site);
|
||||||
|
|
||||||
|
switch (get_class($this->service->type)) {
|
||||||
|
case Service\Broadband::class:
|
||||||
|
$subject = sprintf('Cancel BROADBAND: %s',$this->service->type->service_address);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Service\Phone::class:
|
||||||
|
$subject = sprintf('Cancel PHONE: %s',$this->service->type->service_number);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$subject = 'Cancel Service Request';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this
|
||||||
|
->markdown('email.admin.service.cancel')
|
||||||
|
->subject($subject)
|
||||||
|
->with(['site'=>$this->service->site]);
|
||||||
|
}
|
||||||
|
}
|
59
app/Mail/ChangeRequest.php
Normal file
59
app/Mail/ChangeRequest.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Mail;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Mail\Mailable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
|
use App\Models\Service;
|
||||||
|
|
||||||
|
class ChangeRequest extends Mailable
|
||||||
|
{
|
||||||
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public Service $service;
|
||||||
|
public string $notes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new message instance.
|
||||||
|
*
|
||||||
|
* @param Service $o
|
||||||
|
* @param string $notes
|
||||||
|
*/
|
||||||
|
public function __construct(Service $o,string $notes='')
|
||||||
|
{
|
||||||
|
$this->service = $o;
|
||||||
|
$this->notes = $notes ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the message.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function build()
|
||||||
|
{
|
||||||
|
Config::set('site',$this->service->site);
|
||||||
|
|
||||||
|
switch (get_class($this->service->type)) {
|
||||||
|
case Service\Broadband::class:
|
||||||
|
$subject = sprintf('Change BROADBAND: %s',$this->service->type->service_address);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Service\Phone::class:
|
||||||
|
$subject = sprintf('Change PHONE: %s',$this->service->type->service_number);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$subject = 'Change Service Request';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this
|
||||||
|
->markdown('email.admin.service.change')
|
||||||
|
->subject($subject)
|
||||||
|
->with(['site'=>$this->service->site]);
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Mail;
|
namespace App\Mail;
|
||||||
|
|
||||||
|
use App\Models\Site;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Mail\Mailable;
|
use Illuminate\Mail\Mailable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
|
|
||||||
@ -14,12 +16,12 @@ class InvoiceEmail extends Mailable
|
|||||||
use Queueable, SerializesModels;
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
public $invoice;
|
public $invoice;
|
||||||
|
public $site;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new message instance.
|
* Create a new message instance.
|
||||||
*
|
*
|
||||||
* @param Invoice $o
|
* @param Invoice $o
|
||||||
* @param string $notes
|
|
||||||
*/
|
*/
|
||||||
public function __construct(Invoice $o)
|
public function __construct(Invoice $o)
|
||||||
{
|
{
|
||||||
@ -33,12 +35,15 @@ class InvoiceEmail extends Mailable
|
|||||||
*/
|
*/
|
||||||
public function build()
|
public function build()
|
||||||
{
|
{
|
||||||
|
Config::set('site',Site::findOrFail($this->invoice->site_id));
|
||||||
|
$this->site = config('site');
|
||||||
|
|
||||||
return $this
|
return $this
|
||||||
->markdown('email.user.invoice')
|
->markdown('email.user.invoice',['site'=>config('site')])
|
||||||
->subject(sprintf( 'Invoice: %s - Total: $%s - Due: %s',
|
->subject(sprintf( 'Invoice: %s - Total: $%s - Due: %s',
|
||||||
$this->invoice->id,
|
$this->invoice->id,
|
||||||
number_format($this->invoice->total,2),
|
number_format($this->invoice->total,2),
|
||||||
$this->invoice->date_due))
|
$this->invoice->due_at->format('Y-m-d')))
|
||||||
->with([
|
->with([
|
||||||
'user'=>$this->invoice->account->user,
|
'user'=>$this->invoice->account->user,
|
||||||
'site'=>$this->invoice->account->user->site,
|
'site'=>$this->invoice->account->user->site,
|
||||||
|
@ -3,9 +3,10 @@
|
|||||||
namespace App\Mail;
|
namespace App\Mail;
|
||||||
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Mail\Mailable;
|
use Illuminate\Mail\Mailable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
|
|
||||||
@ -13,8 +14,8 @@ class OrderRequest extends Mailable
|
|||||||
{
|
{
|
||||||
use Queueable, SerializesModels;
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
public $service;
|
public Service $service;
|
||||||
public $notes;
|
public string $notes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new message instance.
|
* Create a new message instance.
|
||||||
@ -22,7 +23,7 @@ class OrderRequest extends Mailable
|
|||||||
* @param Service $o
|
* @param Service $o
|
||||||
* @param string $notes
|
* @param string $notes
|
||||||
*/
|
*/
|
||||||
public function __construct(Service $o,$notes='')
|
public function __construct(Service $o,string $notes='')
|
||||||
{
|
{
|
||||||
$this->service = $o;
|
$this->service = $o;
|
||||||
$this->notes = $notes;
|
$this->notes = $notes;
|
||||||
@ -35,14 +36,15 @@ class OrderRequest extends Mailable
|
|||||||
*/
|
*/
|
||||||
public function build()
|
public function build()
|
||||||
{
|
{
|
||||||
switch (get_class($this->service->type))
|
Config::set('site',$this->service->site);
|
||||||
{
|
|
||||||
case 'App\Models\Service\Adsl':
|
switch (get_class($this->service->type)) {
|
||||||
$subject = sprintf('NBN: %s',$this->service->type->service_address);
|
case Service\Broadband::class:
|
||||||
|
$subject = sprintf('Order BROADBAND: %s',$this->service->type->service_address);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'App\Models\Service\Voip':
|
case Service\Phone::class:
|
||||||
$subject = sprintf('VOIP: %s',$this->service->type->service_number);
|
$subject = sprintf('Order PHONE: %s',$this->service->type->service_number);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -6,6 +6,7 @@ use Illuminate\Bus\Queueable;
|
|||||||
use Illuminate\Mail\Mailable;
|
use Illuminate\Mail\Mailable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
|
|
||||||
@ -13,8 +14,8 @@ class OrderRequestApprove extends Mailable
|
|||||||
{
|
{
|
||||||
use Queueable, SerializesModels;
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
public $service;
|
public Service $service;
|
||||||
public $notes;
|
public string $notes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new message instance.
|
* Create a new message instance.
|
||||||
@ -22,7 +23,7 @@ class OrderRequestApprove extends Mailable
|
|||||||
* @param Service $o
|
* @param Service $o
|
||||||
* @param string $notes
|
* @param string $notes
|
||||||
*/
|
*/
|
||||||
public function __construct(Service $o,$notes='')
|
public function __construct(Service $o,string $notes='')
|
||||||
{
|
{
|
||||||
$this->service = $o;
|
$this->service = $o;
|
||||||
$this->notes = $notes;
|
$this->notes = $notes;
|
||||||
@ -35,12 +36,14 @@ class OrderRequestApprove extends Mailable
|
|||||||
*/
|
*/
|
||||||
public function build()
|
public function build()
|
||||||
{
|
{
|
||||||
switch ($this->service->category)
|
Config::set('site',$this->service->site);
|
||||||
{
|
|
||||||
case 'ADSL': $subject = sprintf('%s: %s',$this->service->category,$this->service->service_adsl->service_address);
|
// @todo This is not consistent with Cancel/Change Request
|
||||||
|
switch ($this->service->category) {
|
||||||
|
case 'broadband': $subject = sprintf('%s: %s',$this->service->category,$this->service->type->service_address);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'VOIP': $subject = sprintf('%s: %s',$this->service->category,$this->service->service_voip->service_number);
|
case 'phone': $subject = sprintf('%s: %s',$this->service->category,$this->service->type->service_number);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -6,6 +6,7 @@ use Illuminate\Bus\Queueable;
|
|||||||
use Illuminate\Mail\Mailable;
|
use Illuminate\Mail\Mailable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
|
|
||||||
@ -13,15 +14,15 @@ class OrderRequestReject extends Mailable
|
|||||||
{
|
{
|
||||||
use Queueable, SerializesModels;
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
public $service;
|
public Service $service;
|
||||||
public $reason;
|
public string $reason;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new message instance.
|
* Create a new message instance.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct(Service $o,$reason)
|
public function __construct(Service $o,string $reason)
|
||||||
{
|
{
|
||||||
$this->service = $o;
|
$this->service = $o;
|
||||||
$this->reason = $reason;
|
$this->reason = $reason;
|
||||||
@ -34,6 +35,8 @@ class OrderRequestReject extends Mailable
|
|||||||
*/
|
*/
|
||||||
public function build()
|
public function build()
|
||||||
{
|
{
|
||||||
|
Config::set('site',$this->service->site);
|
||||||
|
|
||||||
return $this
|
return $this
|
||||||
->markdown('email.admin.order.reject')
|
->markdown('email.admin.order.reject')
|
||||||
->subject(sprintf('Your order: #%s was rejected',$this->service->id))
|
->subject(sprintf('Your order: #%s was rejected',$this->service->id))
|
||||||
|
@ -5,26 +5,28 @@ namespace App\Mail;
|
|||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Mail\Mailable;
|
use Illuminate\Mail\Mailable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
use App\Models\{AccountOauth,User};
|
use App\Models\{Site,User,UserOauth};
|
||||||
|
|
||||||
class SocialLink extends Mailable
|
class SocialLink extends Mailable
|
||||||
{
|
{
|
||||||
use Queueable, SerializesModels;
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
public $token;
|
public string $token;
|
||||||
public $user;
|
public Site $site;
|
||||||
|
public ?User $user;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new message instance.
|
* Create a new message instance.
|
||||||
*
|
*
|
||||||
* @param User $o
|
* @param UserOauth $o
|
||||||
* @param string $token
|
|
||||||
*/
|
*/
|
||||||
public function __construct(AccountOauth $o)
|
public function __construct(UserOauth $o)
|
||||||
{
|
{
|
||||||
|
$this->site = $o->site;
|
||||||
$this->token = $o->link_token;
|
$this->token = $o->link_token;
|
||||||
$this->user = $o->account->user;
|
$this->user = $o->user;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,11 +36,13 @@ class SocialLink extends Mailable
|
|||||||
*/
|
*/
|
||||||
public function build()
|
public function build()
|
||||||
{
|
{
|
||||||
|
Config::set('site',$this->site);
|
||||||
|
|
||||||
return $this
|
return $this
|
||||||
->markdown('email.system.social_link')
|
->markdown('email.system.social_link')
|
||||||
->subject('Link your Account')
|
->subject('Link your Account')
|
||||||
->with([
|
->with([
|
||||||
'site'=>$this->user->site,
|
'site'=>$this->site,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,6 +6,7 @@ use Illuminate\Bus\Queueable;
|
|||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Mail\Mailable;
|
use Illuminate\Mail\Mailable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
|
||||||
@ -13,6 +14,8 @@ class TestEmail extends Mailable
|
|||||||
{
|
{
|
||||||
use Queueable, SerializesModels;
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public User $user;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new message instance.
|
* Create a new message instance.
|
||||||
*
|
*
|
||||||
@ -30,6 +33,8 @@ class TestEmail extends Mailable
|
|||||||
*/
|
*/
|
||||||
public function build()
|
public function build()
|
||||||
{
|
{
|
||||||
|
Config::set('site',$this->user->site);
|
||||||
|
|
||||||
return $this
|
return $this
|
||||||
->markdown('email.system.test_email')
|
->markdown('email.system.test_email')
|
||||||
->subject('Just a test...')
|
->subject('Just a test...')
|
||||||
|
@ -2,25 +2,28 @@
|
|||||||
|
|
||||||
namespace App\Mail;
|
namespace App\Mail;
|
||||||
|
|
||||||
use App\Models\Site;
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Mail\Mailable;
|
use Illuminate\Mail\Mailable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
use App\Models\AdslSupplier;
|
use App\Models\{Supplier,Site};
|
||||||
|
|
||||||
class TrafficMismatch extends Mailable
|
class TrafficMismatch extends Mailable
|
||||||
{
|
{
|
||||||
use Queueable, SerializesModels;
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public Supplier $aso;
|
||||||
|
public Carbon $date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new message instance.
|
* Create a new message instance.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct(AdslSupplier $o,Carbon $date)
|
public function __construct(Supplier $o,Carbon $date)
|
||||||
{
|
{
|
||||||
$this->aso = $o;
|
$this->aso = $o;
|
||||||
$this->date = $date;
|
$this->date = $date;
|
||||||
@ -33,11 +36,13 @@ class TrafficMismatch extends Mailable
|
|||||||
*/
|
*/
|
||||||
public function build()
|
public function build()
|
||||||
{
|
{
|
||||||
|
Config::set('site',$x=Site::find(1)); // @todo To auto determine;
|
||||||
|
|
||||||
return $this
|
return $this
|
||||||
->markdown('email.system.broadband_traffic_mismatch')
|
->markdown('email.system.broadband_traffic_mismatch')
|
||||||
->subject('Traffic Mismatch for '.$this->date)
|
->subject('Traffic Mismatch for '.$this->date)
|
||||||
->with([
|
->with([
|
||||||
'site'=>Site::find(1), // @todo To auto determine
|
'site'=>$x,
|
||||||
'date'=>$this->date,
|
'date'=>$this->date,
|
||||||
'aso'=>$this->aso,
|
'aso'=>$this->aso,
|
||||||
]);
|
]);
|
||||||
|
@ -2,54 +2,71 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use Leenooks\Traits\ScopeActive;
|
use Leenooks\Traits\ScopeActive;
|
||||||
|
|
||||||
use App\Interfaces\IDs;
|
use App\Interfaces\IDs;
|
||||||
use App\Traits\NextKey;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Account
|
* Class Account
|
||||||
* Service Accounts
|
* Service Accounts
|
||||||
*
|
*
|
||||||
* Attributes for accounts:
|
* Attributes for accounts:
|
||||||
* + lid: : Local ID for account
|
* + lid : Local ID for account
|
||||||
* + sid: : System ID for account
|
* + sid : System ID for account
|
||||||
*
|
* + name : Account Name
|
||||||
* @package App\Models
|
* + taxes : Taxes Applicable to this account
|
||||||
*/
|
*/
|
||||||
class Account extends Model implements IDs
|
class Account extends Model implements IDs
|
||||||
{
|
{
|
||||||
use HasFactory,NextKey,ScopeActive;
|
use HasFactory,ScopeActive;
|
||||||
|
|
||||||
const RECORD_ID = 'account';
|
/* STATIC */
|
||||||
public $incrementing = FALSE;
|
|
||||||
|
|
||||||
const CREATED_AT = 'date_orig';
|
public static function InvoicesCredit(Collection $invoices=NULL): Collection
|
||||||
const UPDATED_AT = 'date_last';
|
{
|
||||||
|
return (new self)
|
||||||
|
->invoiceSummaryCredit($invoices,TRUE)
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
|
||||||
protected $appends = [
|
public static function InvoicesDue(Collection $invoices=NULL): Collection
|
||||||
'active_display',
|
{
|
||||||
'name',
|
return (new self)
|
||||||
'services_count_html',
|
->invoiceSummaryDue($invoices,TRUE)
|
||||||
'switch_url',
|
->get();
|
||||||
];
|
}
|
||||||
|
|
||||||
public $dateFormat = 'U';
|
/* INTERFACES */
|
||||||
|
|
||||||
protected $visible = [
|
public function getLIDAttribute(): string
|
||||||
'id',
|
{
|
||||||
'active_display',
|
return sprintf('%04s',$this->id);
|
||||||
'name',
|
}
|
||||||
'services_count_html',
|
|
||||||
'switch_url',
|
public function getSIDAttribute(): string
|
||||||
];
|
{
|
||||||
|
return sprintf('%02s-%s',$this->site_id,$this->getLIDAttribute());
|
||||||
|
}
|
||||||
|
|
||||||
/* RELATIONS */
|
/* RELATIONS */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the country the user belongs to
|
* Charges assigned to this account
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||||
|
*/
|
||||||
|
public function charges()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Charge::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Country this account belongs to
|
||||||
*/
|
*/
|
||||||
public function country()
|
public function country()
|
||||||
{
|
{
|
||||||
@ -61,28 +78,87 @@ class Account extends Model implements IDs
|
|||||||
return $this->belongsToMany(External\Integrations::class,'external_account',NULL,'external_integration_id');
|
return $this->belongsToMany(External\Integrations::class,'external_account',NULL,'external_integration_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function group()
|
||||||
|
{
|
||||||
|
return $this->hasOneThrough(Group::class,AccountGroup::class,'account_id','id','id','group_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoices created for this account
|
||||||
|
*
|
||||||
|
* @todo This needs to be optimised, to only return outstanding invoices and invoices for a specific age (eg: 2 years worth)
|
||||||
|
*/
|
||||||
public function invoices()
|
public function invoices()
|
||||||
{
|
{
|
||||||
return $this->hasMany(Invoice::class);
|
return $this->hasMany(Invoice::class)
|
||||||
|
->with(['items.taxes','paymentitems.payment']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function language()
|
/**
|
||||||
|
* Relation to only return active invoices
|
||||||
|
*
|
||||||
|
* @todo Only return active invoice_items
|
||||||
|
*/
|
||||||
|
public function invoices_active()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Language::class);
|
return $this->invoices()
|
||||||
|
->active();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Payments received and assigned to this account
|
||||||
|
*/
|
||||||
public function payments()
|
public function payments()
|
||||||
{
|
{
|
||||||
return $this->hasMany(Payment::class);
|
return $this->hasMany(Payment::class)
|
||||||
|
->with(['items']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function services($active=FALSE)
|
/**
|
||||||
|
* Relation to only return active payments
|
||||||
|
*
|
||||||
|
* @todo Only return active payment_items
|
||||||
|
*/
|
||||||
|
public function payments_active()
|
||||||
{
|
{
|
||||||
$query = $this->hasMany(Service::class);
|
return $this->payments()
|
||||||
|
->active();
|
||||||
return $active ? $query->active() : $query;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function providers()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(ProviderOauth::class,'account__provider')
|
||||||
|
->where('account__provider.site_id',$this->site_id)
|
||||||
|
->withPivot('ref','synctoken','created_at','updated_at');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Services assigned to this account
|
||||||
|
*/
|
||||||
|
public function services()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Service::class)
|
||||||
|
->with(['product.translate','product.type.supplied']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relation to only return active services
|
||||||
|
*/
|
||||||
|
public function services_active()
|
||||||
|
{
|
||||||
|
return $this->services()
|
||||||
|
->active();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function taxes()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Tax::class,'country_id','country_id')
|
||||||
|
->select(['id','zone','rate','country_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User that owns this account
|
||||||
|
*/
|
||||||
public function user()
|
public function user()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(User::class);
|
return $this->belongsTo(User::class);
|
||||||
@ -95,7 +171,7 @@ class Account extends Model implements IDs
|
|||||||
*
|
*
|
||||||
* @param $query
|
* @param $query
|
||||||
* @param string $term
|
* @param string $term
|
||||||
* @return
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function scopeSearch($query,string $term)
|
public function scopeSearch($query,string $term)
|
||||||
{
|
{
|
||||||
@ -115,117 +191,54 @@ class Account extends Model implements IDs
|
|||||||
|
|
||||||
/* ATTRIBUTES */
|
/* ATTRIBUTES */
|
||||||
|
|
||||||
public function getActiveDisplayAttribute($value)
|
|
||||||
{
|
|
||||||
return sprintf('<span class="btn-sm btn-block btn-%s text-center">%s</span>',$this->active ? 'success' : 'danger',$this->active ? 'Active' : 'Inactive');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated use getAIDAttribute()
|
|
||||||
*/
|
|
||||||
public function getAccountIdAttribute()
|
|
||||||
{
|
|
||||||
return $this->getAIDAttribute();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated use getUrlAdminAttribute()
|
|
||||||
*/
|
|
||||||
public function getAccountIdUrlAttribute()
|
|
||||||
{
|
|
||||||
return $this->getUrlAdminAttribute();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the address for the account
|
* Get the address for the account
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
|
* @todo Change this to return a collection
|
||||||
*/
|
*/
|
||||||
public function getAddressAttribute(): array
|
public function getAddressAttribute(): array
|
||||||
{
|
{
|
||||||
return [
|
return collect([
|
||||||
$this->address1,
|
'address1' => $this->address1,
|
||||||
$this->address2,
|
'address2' => $this->address2,
|
||||||
sprintf('%s %s %s',$this->city.(($this->state OR $this->zip) ? ',' : ''),$this->state,$this->zip)
|
'location' => sprintf('%s %s %s',
|
||||||
];
|
$this->city.(($this->state || $this->zip) ? ',' : ''),
|
||||||
|
$this->state,
|
||||||
|
$this->zip)
|
||||||
|
])
|
||||||
|
->filter()
|
||||||
|
->values()
|
||||||
|
->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the Account Unique Identifier
|
* Return the account name
|
||||||
* @return string
|
|
||||||
* @deprecated use getSIDAttribute()
|
|
||||||
*/
|
|
||||||
public function getAIDAttribute()
|
|
||||||
{
|
|
||||||
return $this->getSIDAttribute();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Account Local ID
|
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getLIDAttribute(): string
|
public function getNameAttribute(): string
|
||||||
{
|
{
|
||||||
return sprintf('%04s',$this->id);
|
return $this->company ?: ($this->user_id ? $this->user->getSurFirstNameAttribute() : 'LID:'.$this->id);
|
||||||
}
|
|
||||||
|
|
||||||
public function getNameAttribute()
|
|
||||||
{
|
|
||||||
return $this->company ?: ($this->user_id ? $this->user->SurFirstName : 'AID:'.$this->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getServicesCountHtmlAttribute()
|
|
||||||
{
|
|
||||||
return sprintf('%s <small>/%s</small>',$this->services()->noEagerLoads()->where('active',TRUE)->count(),$this->services()->noEagerLoads()->count());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Account System ID
|
* Return the type of account this is - if it has a company name, then its a business account.
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getSIDAttribute(): string
|
public function getTypeAttribute(): string
|
||||||
{
|
|
||||||
return sprintf('%02s-%s',$this->site_id,$this->getLIDAttribute());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSwitchUrlAttribute()
|
|
||||||
{
|
|
||||||
return sprintf('<a href="/r/switch/start/%s"><i class="fas fa-external-link-alt"></i></a>',$this->user_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTypeAttribute()
|
|
||||||
{
|
{
|
||||||
return $this->company ? 'Business' : 'Private';
|
return $this->company ? 'Business' : 'Private';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/* METHODS */
|
||||||
* Return the Admin URL to manage the account
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getUrlAdminAttribute(): string
|
|
||||||
{
|
|
||||||
return sprintf('<a href="/r/account/view/%s">%s</a>',$this->id,$this->account_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the User URL to manage the account
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getUrlUserAttribute(): string
|
|
||||||
{
|
|
||||||
return sprintf('<a href="/u/account/view/%s">%s</a>',$this->id,$this->account_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* GENERAL METHODS */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the due invoices on an account
|
* Get the due invoices on an account
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
|
* @deprecated use invoiceSummary->filter(_balance > 0)
|
||||||
*/
|
*/
|
||||||
public function dueInvoices()
|
public function dueInvoices()
|
||||||
{
|
{
|
||||||
@ -235,17 +248,97 @@ class Account extends Model implements IDs
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the external account ID for a specific integration
|
* List of invoices (summary) for this account
|
||||||
*
|
*
|
||||||
* @param External\Integrations $o
|
* @param Collection|NULL $invoices
|
||||||
* @return mixed
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
public function ExternalAccounting(External\Integrations $o)
|
public function invoiceSummary(Collection $invoices=NULL,bool $all=FALSE): Builder
|
||||||
{
|
{
|
||||||
return $this
|
return (new Invoice)
|
||||||
->external()
|
->select([
|
||||||
->where('id','=',$o->id)
|
'invoices.account_id',
|
||||||
->where('site_id','=',$this->site_id)
|
'invoices.id as id',
|
||||||
->first();
|
DB::raw('SUM(item) AS _item'),
|
||||||
|
DB::raw('SUM(tax) AS _tax'),
|
||||||
|
DB::raw('SUM(payments) AS _payment'),
|
||||||
|
DB::raw('SUM(discount)+COALESCE(invoices.discount_amt,0) AS _discount'),
|
||||||
|
DB::raw('SUM(item_total) AS _item_total'),
|
||||||
|
DB::raw('SUM(payment_fees) AS _payment_fee'),
|
||||||
|
DB::raw('ROUND(CAST(SUM(item_total)-COALESCE(invoices.discount_amt,0) AS NUMERIC),2) AS _total'),
|
||||||
|
DB::raw('ROUND(CAST(SUM(item_total)-COALESCE(invoices.discount_amt,0)-SUM(payments) AS NUMERIC),2) AS _balance'),
|
||||||
|
'invoices.due_at',
|
||||||
|
'invoices.created_at',
|
||||||
|
])
|
||||||
|
->from(
|
||||||
|
(new Payment)
|
||||||
|
->select([
|
||||||
|
'invoice_id',
|
||||||
|
DB::raw('0 as item'),
|
||||||
|
DB::raw('0 as tax'),
|
||||||
|
DB::raw('0 as discount'),
|
||||||
|
DB::raw('0 as item_total'),
|
||||||
|
DB::raw('SUM(amount) AS payments'),
|
||||||
|
DB::raw('SUM(fees_amt) AS payment_fees'),
|
||||||
|
])
|
||||||
|
->leftjoin('payment_items',['payment_items.payment_id'=>'payments.id'])
|
||||||
|
->where('payments.active',TRUE)
|
||||||
|
->where('payment_items.active',TRUE)
|
||||||
|
->groupBy(['payment_items.invoice_id'])
|
||||||
|
->union(
|
||||||
|
(new InvoiceItem)
|
||||||
|
->select([
|
||||||
|
'invoice_id',
|
||||||
|
DB::raw('ROUND(CAST(SUM(quantity*price_base) AS NUMERIC),2) AS item'),
|
||||||
|
DB::raw('ROUND(CAST(SUM(amount) AS NUMERIC),2) AS tax'),
|
||||||
|
DB::raw('ROUND(CAST(SUM(COALESCE(invoice_items.discount_amt,0)) AS NUMERIC),2) AS discount'),
|
||||||
|
DB::raw('ROUND(CAST(SUM(ROUND(CAST(quantity*price_base AS NUMERIC),2))+SUM(ROUND(CAST(amount AS NUMERIC),2))-SUM(ROUND(CAST(COALESCE(invoice_items.discount_amt,0) AS NUMERIC),2)) AS NUMERIC),2) AS item_total'),
|
||||||
|
DB::raw('0 as payments'),
|
||||||
|
DB::raw('0 as payment_fees'),
|
||||||
|
])
|
||||||
|
->leftjoin('invoice_item_taxes',['invoice_item_taxes.invoice_item_id'=>'invoice_items.id'])
|
||||||
|
->rightjoin('invoices',['invoices.id'=>'invoice_items.invoice_id'])
|
||||||
|
->where('invoice_items.active',TRUE)
|
||||||
|
->where('invoice_item_taxes.active',TRUE)
|
||||||
|
->where('invoices.active',TRUE)
|
||||||
|
->groupBy(['invoice_items.invoice_id']),
|
||||||
|
),'p')
|
||||||
|
->join('invoices',['invoices.id'=>'invoice_id'])
|
||||||
|
->when(($all === FALSE),fn($query)=>$query->where('invoices.account_id',$this->id))
|
||||||
|
->orderBy('due_at')
|
||||||
|
->groupBy(['invoices.account_id','invoices.id','invoices.created_at','invoices.due_at','invoices.discount_amt'])
|
||||||
|
->with(['account']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function invoiceSummaryDue(Collection $invoices=NULL,bool $all=FALSE): Builder
|
||||||
|
{
|
||||||
|
return $this->invoiceSummary($invoices,$all)
|
||||||
|
->havingRaw('ROUND(CAST(SUM(item_total)-COALESCE(invoices.discount_amt,0)-SUM(payments) AS NUMERIC),2) > 0');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function invoiceSummaryCredit(Collection $invoices=NULL,bool $all=FALSE): Builder
|
||||||
|
{
|
||||||
|
return $this->invoiceSummary($invoices,$all)
|
||||||
|
->havingRaw('ROUND(CAST(SUM(item_total)-COALESCE(invoices.discount_amt,0)-SUM(payments) AS NUMERIC),2) < 0');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function invoiceSummaryPast(Collection $invoices=NULL,bool $all=FALSE): Builder
|
||||||
|
{
|
||||||
|
return $this->invoiceSummary($invoices,$all)
|
||||||
|
->join('payment_items',['payment_items.invoice_id'=>'invoices.id'])
|
||||||
|
->join('payments',['payments.id'=>'payment_items.payment_id'])
|
||||||
|
->addSelect(DB::raw('max(paid_at) as _paid_at'))
|
||||||
|
->havingRaw('ROUND(CAST(SUM(item_total)-COALESCE(invoices.discount_amt,0)-SUM(payments) AS NUMERIC),2) <= 0');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the taxed value of a value
|
||||||
|
*
|
||||||
|
* @param float $value
|
||||||
|
* @return float
|
||||||
|
*/
|
||||||
|
public function taxed(float $value): float
|
||||||
|
{
|
||||||
|
return Tax::calc($value,$this->taxes);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,7 +4,7 @@ namespace App\Models;
|
|||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class HostServer extends Model
|
class AccountGroup extends Model
|
||||||
{
|
{
|
||||||
protected $table = 'ab_host_server';
|
protected $table = 'account_group';
|
||||||
}
|
}
|
@ -1,46 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Models;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
|
|
||||||
use App\Traits\NextKey;
|
|
||||||
|
|
||||||
class AccountOauth extends Model
|
|
||||||
{
|
|
||||||
use NextKey;
|
|
||||||
const RECORD_ID = 'account_oauth';
|
|
||||||
public $incrementing = FALSE;
|
|
||||||
|
|
||||||
protected $table = 'ab_account_oauth';
|
|
||||||
const CREATED_AT = 'date_orig';
|
|
||||||
const UPDATED_AT = 'date_last';
|
|
||||||
public $dateFormat = 'U';
|
|
||||||
|
|
||||||
protected $casts = [
|
|
||||||
'oauth_data'=>'array',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function account()
|
|
||||||
{
|
|
||||||
return $this->belongsTo(Account::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function site()
|
|
||||||
{
|
|
||||||
return $this->belongsTo(Site::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function User()
|
|
||||||
{
|
|
||||||
return $this->belongsTo(User::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a link token to use when validating account.
|
|
||||||
*/
|
|
||||||
public function getLinkTokenAttribute()
|
|
||||||
{
|
|
||||||
return strtoupper(substr(md5($this->id.$this->date_last),0,8));
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user