Inuit sync of tax, product accounting, accounts and invoices
This commit is contained in:
parent
e2d8f8a096
commit
ad2f6f3a7f
@ -5,58 +5,58 @@ namespace App\Console\Commands;
|
|||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\Config;
|
use Illuminate\Support\Facades\Config;
|
||||||
use Intuit\Jobs\AccountingCustomerUpdate;
|
use Intuit\Jobs\AccountingCustomerUpdate;
|
||||||
use Intuit\Models\Customer;
|
use Intuit\Models\Customer as AccAccount;
|
||||||
|
|
||||||
use App\Models\{Account,ProviderOauth,Site,User};
|
use App\Models\{Account,ProviderOauth,Site,User};
|
||||||
|
|
||||||
class AccountingAccountAdd extends Command
|
class AccountingAccountAdd extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'accounting:account:add'
|
protected $signature = 'accounting:account:add'
|
||||||
.' {siteid : Site ID}'
|
.' {siteid : Site ID}'
|
||||||
.' {provider : Provider Name}'
|
.' {provider : Provider Name}'
|
||||||
.' {user : User Email}'
|
.' {user : User Email}'
|
||||||
.' {id : Account ID}';
|
.' {id : Account ID}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $description = 'Add an account to the accounting provider';
|
protected $description = 'Add an account to the accounting provider';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$site = Site::findOrFail($this->argument('siteid'));
|
$site = Site::findOrFail($this->argument('siteid'));
|
||||||
Config::set('site',$site);
|
Config::set('site',$site);
|
||||||
|
|
||||||
$so = ProviderOauth::where('name',$this->argument('provider'))->singleOrFail();
|
|
||||||
$uo = User::where('email',$this->argument('user'))->singleOrFail();
|
$uo = User::where('email',$this->argument('user'))->singleOrFail();
|
||||||
|
|
||||||
if (($x=$so->tokens->where('user_id',$uo->id))->count() !== 1)
|
$so = ProviderOauth::where('name',$this->argument('provider'))->singleOrFail();
|
||||||
|
if (! ($to=$so->token($uo)))
|
||||||
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
||||||
|
|
||||||
$ao = Account::findOrFail($this->argument('id'));
|
$o = Account::findOrFail($this->argument('id'));
|
||||||
|
|
||||||
$customer = new Customer;
|
$acc = new AccAccount;
|
||||||
$customer->PrimaryEmailAddr = (object)['Address'=>$ao->user->email];
|
$acc->PrimaryEmailAddr = (object)['Address'=>$o->user->email];
|
||||||
$customer->ResaleNum = $ao->sid;
|
$acc->ResaleNum = $o->sid;
|
||||||
$customer->GivenName = $ao->user->firstname;
|
$acc->GivenName = $o->user->firstname;
|
||||||
$customer->FamilyName = $ao->user->lastname;
|
$acc->FamilyName = $o->user->lastname;
|
||||||
$customer->CompanyName = $ao->name;
|
$acc->CompanyName = $o->name;
|
||||||
$customer->DisplayName = $ao->name;
|
$acc->DisplayName = $o->name;
|
||||||
$customer->FullyQualifiedName = $ao->name;
|
$acc->FullyQualifiedName = $o->name;
|
||||||
$customer->Active = (bool)$ao->active;
|
$acc->Active = (bool)$o->active;
|
||||||
|
|
||||||
return AccountingCustomerUpdate::dispatchSync($x->pop(),$customer);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,41 +8,44 @@ use Illuminate\Support\Facades\Config;
|
|||||||
use App\Models\{ProviderOauth,Site,User};
|
use App\Models\{ProviderOauth,Site,User};
|
||||||
use App\Jobs\AccountingAccountSync as Job;
|
use App\Jobs\AccountingAccountSync as Job;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronise Customers with Accounts
|
||||||
|
*/
|
||||||
class AccountingAccountSync extends Command
|
class AccountingAccountSync extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'accounting:account:sync'
|
protected $signature = 'accounting:account:sync'
|
||||||
.' {siteid : Site ID}'
|
.' {siteid : Site ID}'
|
||||||
.' {provider : Provider Name}'
|
.' {provider : Provider Name}'
|
||||||
.' {user : User Email}';
|
.' {user : User Email}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $description = 'Synchronise accounts with accounting system';
|
protected $description = 'Synchronise accounts with accounting system';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$site = Site::findOrFail($this->argument('siteid'));
|
$site = Site::findOrFail($this->argument('siteid'));
|
||||||
Config::set('site',$site);
|
Config::set('site',$site);
|
||||||
|
|
||||||
$so = ProviderOauth::where('name',$this->argument('provider'))->singleOrFail();
|
|
||||||
$uo = User::where('email',$this->argument('user'))->singleOrFail();
|
$uo = User::where('email',$this->argument('user'))->singleOrFail();
|
||||||
|
|
||||||
if (($x=$so->tokens->where('user_id',$uo->id))->count() !== 1)
|
$so = ProviderOauth::where('name',$this->argument('provider'))->singleOrFail();
|
||||||
|
if (! ($to=$so->token($uo)))
|
||||||
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
||||||
|
|
||||||
Job::dispatchSync($x->pop());
|
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('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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -24,20 +24,18 @@ class AccountingController extends Controller
|
|||||||
*/
|
*/
|
||||||
public static function list(string $provider): Collection
|
public static function list(string $provider): Collection
|
||||||
{
|
{
|
||||||
$so = ProviderOauth::where('name',$provider)->singleOrFail();
|
|
||||||
// @todo This should be a variable
|
// @todo This should be a variable
|
||||||
$uo = User::findOrFail(1);
|
$uo = User::findOrFail(1);
|
||||||
|
|
||||||
if (($x=$so->tokens->where('user_id',$uo->id))->count() !== 1)
|
$so = ProviderOauth::where('name',$provider)->singleOrFail();
|
||||||
|
if (! ($to=$so->token($uo)))
|
||||||
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
|
||||||
|
|
||||||
$to = $x->pop();
|
$api = $to->API();
|
||||||
|
|
||||||
$api = $so->API($to,TRUE); // @todo Remove TRUE
|
|
||||||
|
|
||||||
return $api->getItems()
|
return $api->getItems()
|
||||||
->pluck('pid','FullyQualifiedName')
|
->pluck('pid','Id')
|
||||||
->transform(function($item,$value) { return ['id'=>$item,'value'=>$value]; })
|
->transform(function($item,$value) { return ['id'=>$value,'value'=>$item]; })
|
||||||
->values();
|
->values();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ class ProductController extends Controller
|
|||||||
|
|
||||||
public function details_addedit(ProductAddEdit $request,Product $o)
|
public function details_addedit(ProductAddEdit $request,Product $o)
|
||||||
{
|
{
|
||||||
foreach ($request->except(['_token','submit','translate']) as $key => $item)
|
foreach ($request->except(['_token','submit','translate','accounting']) as $key => $item)
|
||||||
$o->{$key} = $item;
|
$o->{$key} = $item;
|
||||||
|
|
||||||
$o->active = (bool)$request->active;
|
$o->active = (bool)$request->active;
|
||||||
@ -122,6 +122,15 @@ class ProductController extends Controller
|
|||||||
|
|
||||||
$o->translate()->save($oo);
|
$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()
|
return redirect()->back()
|
||||||
->with('success','Product saved');
|
->with('success','Product saved');
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ class ProductAddEdit extends FormRequest
|
|||||||
'active' => 'sometimes|accepted',
|
'active' => 'sometimes|accepted',
|
||||||
'model' => 'sometimes|string', // @todo Check that it is a valid model type
|
'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
|
'model_id' => 'sometimes|int', // @todo Check that it is a valid model type
|
||||||
'accounting' => 'nullable|string', // @todo Validate that the value is in the accounting system
|
'accounting' => 'nullable|array', // @todo Validate that the value is in the accounting system
|
||||||
'pricing' => 'required|array', // @todo Validate the elements in the pricing
|
'pricing' => 'required|array', // @todo Validate the elements in the pricing
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -14,82 +14,94 @@ use Intuit\Jobs\AccountingCustomerUpdate;
|
|||||||
|
|
||||||
use App\Models\{Account,ProviderToken};
|
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
|
class AccountingAccountSync implements ShouldQueue
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
private const LOGKEY = 'JAS';
|
private const LOGKEY = 'JAS';
|
||||||
|
|
||||||
private ProviderToken $to;
|
private ProviderToken $to;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
* @return void
|
* @param ProviderToken $to
|
||||||
*/
|
*/
|
||||||
public function __construct(ProviderToken $to)
|
public function __construct(ProviderToken $to)
|
||||||
{
|
{
|
||||||
$this->to = $to;
|
$this->to = $to;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the job.
|
* Execute the job.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
* @throws \Exception
|
||||||
public function handle()
|
*/
|
||||||
{
|
public function handle()
|
||||||
$api = $this->to->provider->API($this->to);
|
{
|
||||||
$accounts = Account::with(['user'])->get();
|
$api = $this->to->API();
|
||||||
|
$ref = Account::select('id','site_id','company','user_id')->with(['user'])->get();
|
||||||
|
|
||||||
foreach ($api->getCustomers() as $customer) {
|
foreach ($api->getCustomers() as $acc) {
|
||||||
$ao = NULL;
|
$o = NULL;
|
||||||
|
|
||||||
// See if we are already linked
|
// See if we are already linked
|
||||||
if (($x=$this->to->provider->accounts->where('pivot.ref',$customer->id))->count() === 1) {
|
if (($x=$this->to->provider->accounts->where('pivot.ref',$acc->id))->count() === 1) {
|
||||||
$ao = $x->pop();
|
$o = $x->pop();
|
||||||
|
|
||||||
// If not, see if our reference matches
|
// If not, see if our reference matches
|
||||||
} elseif (($x=$accounts->filter(function($item) use ($customer) { return $item->sid == $customer->ref; }))->count() === 1) {
|
} elseif (($x=$ref->filter(function($item) use ($acc) { return $item->sid == $acc->ref; }))->count() === 1) {
|
||||||
$ao = $x->pop();
|
$o = $x->pop();
|
||||||
|
|
||||||
// Look based on Name
|
// Look based on Name
|
||||||
} elseif (($x=$accounts->filter(function($item) use ($customer) { return $item->company == $customer->companyname || $item->name == $customer->fullname || $item->user->email == $customer->email; }))->count() === 1) {
|
} elseif (($x=$ref->filter(function($item) use ($acc) { return $item->company == $acc->companyname || $item->name == $acc->fullname || $item->user->email == $acc->email; }))->count() === 1) {
|
||||||
$ao = $x->pop();
|
$o = $x->pop();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Log not found
|
// Log not found
|
||||||
Log::alert(sprintf('%s:Customer not found [%s:%s]',self::LOGKEY,$customer->id,$customer->DisplayName));
|
Log::alert(sprintf('%s:Customer not found [%s:%s]',self::LOGKEY,$acc->id,$acc->DisplayName));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$ao->providers()->syncWithoutDetaching([
|
$o->providers()->syncWithoutDetaching([
|
||||||
$this->to->provider->id => [
|
$this->to->provider->id => [
|
||||||
'ref' => $customer->id,
|
'ref' => $acc->id,
|
||||||
'synctoken' => $customer->synctoken,
|
'synctoken' => $acc->synctoken,
|
||||||
'created_at'=>Carbon::create($customer->created_at),
|
'created_at'=>Carbon::create($acc->created_at),
|
||||||
'updated_at'=>Carbon::create($customer->updated_at),
|
'updated_at'=>Carbon::create($acc->updated_at),
|
||||||
'site_id'=>$ao->site_id, // @todo See if we can have this handled automatically
|
'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.
|
// Check if QB is out of Sync and update it.
|
||||||
$customer->syncOriginal();
|
$acc->syncOriginal();
|
||||||
$customer->PrimaryEmailAddr = (object)['Address'=>$ao->user->email];
|
$acc->PrimaryEmailAddr = (object)['Address'=>$o->user->email];
|
||||||
$customer->ResaleNum = $ao->sid;
|
$acc->ResaleNum = $o->sid;
|
||||||
$customer->GivenName = $ao->user->firstname;
|
$acc->GivenName = $o->user->firstname;
|
||||||
$customer->FamilyName = $ao->user->lastname;
|
$acc->FamilyName = $o->user->lastname;
|
||||||
$customer->CompanyName = $ao->name;
|
$acc->CompanyName = $o->name;
|
||||||
$customer->DisplayName = $ao->name;
|
$acc->DisplayName = $o->name;
|
||||||
$customer->FullyQualifiedName = $ao->name;
|
$acc->FullyQualifiedName = $o->name;
|
||||||
//$customer->Active = (bool)$ao->active;
|
//$acc->Active = (bool)$o->active; // @todo implement in-activity, but only if all invoices are paid and services cancelled
|
||||||
|
|
||||||
if ($customer->getDirty()) {
|
if ($acc->getDirty()) {
|
||||||
Log::info(sprintf('%s:Customer [%s] (%s:%s) has changed',self::LOGKEY,$ao->sid,$customer->id,$customer->DisplayName),['dirty'=>$customer->getDirty()]);
|
Log::info(sprintf('%s:Customer [%s] (%s:%s) has changed',self::LOGKEY,$o->sid,$acc->id,$acc->DisplayName),['dirty'=>$acc->getDirty()]);
|
||||||
$customer->sparse = 'true';
|
$acc->sparse = 'true';
|
||||||
|
|
||||||
AccountingCustomerUpdate::dispatch($this->to,$customer);
|
AccountingCustomerUpdate::dispatch($this->to,$acc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @todo Identify accounts in our DB that are not in accounting
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -209,6 +209,13 @@ class Invoice extends Model implements IDs
|
|||||||
return $this->hasMany(PaymentItem::class);
|
return $this->hasMany(PaymentItem::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function providers()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(ProviderOauth::class,'invoice__provider')
|
||||||
|
->where('invoice__provider.site_id',$this->site_id)
|
||||||
|
->withPivot('ref','synctoken','created_at','updated_at');
|
||||||
|
}
|
||||||
|
|
||||||
/* SCOPES */
|
/* SCOPES */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,4 +12,11 @@ class InvoiceItemTax extends Model
|
|||||||
|
|
||||||
protected $table = 'invoice_item_taxes';
|
protected $table = 'invoice_item_taxes';
|
||||||
public $timestamps = FALSE;
|
public $timestamps = FALSE;
|
||||||
|
|
||||||
|
/* RELATIONS */
|
||||||
|
|
||||||
|
public function tax()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Tax::class);
|
||||||
|
}
|
||||||
}
|
}
|
@ -16,7 +16,7 @@ use Leenooks\Traits\ScopeActive;
|
|||||||
|
|
||||||
use App\Http\Controllers\AccountingController;
|
use App\Http\Controllers\AccountingController;
|
||||||
use App\Interfaces\{IDs,ProductItem};
|
use App\Interfaces\{IDs,ProductItem};
|
||||||
use App\Traits\{ProductDetails,SiteID};
|
use App\Traits\{ProductDetails,ProviderRef,SiteID};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Product
|
* Class Product
|
||||||
@ -66,7 +66,7 @@ use App\Traits\{ProductDetails,SiteID};
|
|||||||
*/
|
*/
|
||||||
class Product extends Model implements IDs
|
class Product extends Model implements IDs
|
||||||
{
|
{
|
||||||
use Compoships,HasFactory,SiteID,ProductDetails,ScopeActive;
|
use Compoships,HasFactory,SiteID,ProductDetails,ScopeActive,ProviderRef;
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'pricing'=>'collection',
|
'pricing'=>'collection',
|
||||||
@ -106,6 +106,13 @@ class Product extends Model implements IDs
|
|||||||
|
|
||||||
/* RELATIONS */
|
/* RELATIONS */
|
||||||
|
|
||||||
|
public function providers()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(ProviderOauth::class,'product__provider')
|
||||||
|
->where('product__provider.site_id',$this->site_id)
|
||||||
|
->withPivot('ref');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Which services are configured with this product
|
* Which services are configured with this product
|
||||||
*
|
*
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
use App\Traits\SiteID;
|
use App\Traits\SiteID;
|
||||||
@ -23,10 +24,17 @@ class ProviderOauth extends Model
|
|||||||
->withPivot('ref','synctoken','created_at','updated_at');
|
->withPivot('ref','synctoken','created_at','updated_at');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function products()
|
public function invoices()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(Product::class,'product__provider')
|
return $this->belongsToMany(Invoice::class,'invoice__provider')
|
||||||
->where('product__provider.site_id',$this->site_id)
|
->where('invoice__provider.site_id',$this->site_id)
|
||||||
|
->withPivot('ref','synctoken','created_at','updated_at');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function taxes()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Tax::class,'tax__provider')
|
||||||
|
->where('tax__provider.site_id',$this->site_id)
|
||||||
->withPivot('ref','synctoken','created_at','updated_at');
|
->withPivot('ref','synctoken','created_at','updated_at');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,23 +50,30 @@ class ProviderOauth extends Model
|
|||||||
|
|
||||||
/* METHODS */
|
/* METHODS */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
public function api_class(): ?string
|
public function api_class(): ?string
|
||||||
{
|
{
|
||||||
return config('services.provider.'.strtolower($this->name).'.api');
|
return config('services.provider.'.strtolower($this->name).'.api');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function API(ProviderToken $o,bool $tryprod=FALSE): mixed
|
/**
|
||||||
|
* Return a list of the provider OAUTH details
|
||||||
|
*/
|
||||||
|
public static function providers(): Collection
|
||||||
{
|
{
|
||||||
return ($this->api_class() && $o->access_token) ? new ($this->api_class())($o,$tryprod) : NULL;
|
return (new self)::whereIn('name',array_keys(config('services.provider')))->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do we have API details for this supplier
|
* Return the token object for a specific user
|
||||||
*
|
*
|
||||||
* @return bool
|
* @param User $uo
|
||||||
|
* @return ProviderToken|null
|
||||||
*/
|
*/
|
||||||
public function hasAPIdetails(): bool
|
public function token(User $uo): ?ProviderToken
|
||||||
{
|
{
|
||||||
return $this->api_class() && $this->access_token && (! $this->hasAccessTokenExpired());
|
return (($x=$this->tokens->where('user_id',$uo->id))->count() === 1) ? $x->pop() : NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,10 +15,38 @@ class ProviderToken extends ProviderTokenBase
|
|||||||
'refresh_token_expires_at',
|
'refresh_token_expires_at',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
protected $with = ['provider'];
|
||||||
|
|
||||||
/* RELATIONS */
|
/* RELATIONS */
|
||||||
|
|
||||||
public function provider()
|
public function provider()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(ProviderOauth::class,'provider_oauth_id');
|
return $this->belongsTo(ProviderOauth::class,'provider_oauth_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* METHODS */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an API object to interact with this provider
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function API(): mixed
|
||||||
|
{
|
||||||
|
if (! $this->provider->api_class() || ! $this->access_token)
|
||||||
|
throw new \Exception(sprintf('No API details for [%s]',$this->provider->name));
|
||||||
|
|
||||||
|
return new ($this->provider->api_class())($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do we have API details for this provider
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasAPIdetails(): bool
|
||||||
|
{
|
||||||
|
return $this->provider->api_class() && $this->access_token && (! $this->hasAccessTokenExpired());
|
||||||
|
}
|
||||||
}
|
}
|
@ -5,8 +5,12 @@ namespace App\Models;
|
|||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
use App\Traits\ProviderRef;
|
||||||
|
|
||||||
class Tax extends Model
|
class Tax extends Model
|
||||||
{
|
{
|
||||||
|
use ProviderRef;
|
||||||
|
|
||||||
public $timestamps = FALSE;
|
public $timestamps = FALSE;
|
||||||
|
|
||||||
/* RELATIONS */
|
/* RELATIONS */
|
||||||
@ -16,6 +20,12 @@ class Tax extends Model
|
|||||||
return $this->belongsTo(Country::class);
|
return $this->belongsTo(Country::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function providers()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(ProviderOauth::class,'tax__provider')
|
||||||
|
->withPivot('ref','synctoken','created_at','updated_at');
|
||||||
|
}
|
||||||
|
|
||||||
/* METHODS */
|
/* METHODS */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
16
app/Traits/ProviderRef.php
Normal file
16
app/Traits/ProviderRef.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a provider reference ID
|
||||||
|
*/
|
||||||
|
namespace App\Traits;
|
||||||
|
|
||||||
|
use App\Models\ProviderOauth;
|
||||||
|
|
||||||
|
trait ProviderRef
|
||||||
|
{
|
||||||
|
public function provider_ref(ProviderOauth $poo): ?string
|
||||||
|
{
|
||||||
|
return (($x=$this->providers->where('pivot.provider_oauth_id',$poo->id))->count() === 1) ? $x->pop()->pivot->ref : NULL;
|
||||||
|
}
|
||||||
|
}
|
@ -43,16 +43,16 @@ return [
|
|||||||
'redirect' => '/auth/google/callback',
|
'redirect' => '/auth/google/callback',
|
||||||
],
|
],
|
||||||
|
|
||||||
'provider' => [
|
'provider' => [
|
||||||
'intuit' => [
|
'intuit' => [
|
||||||
'api'=> \Intuit\API::class,
|
'api'=> \Intuit\API::class,
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|
||||||
'supplier' => [
|
'supplier' => [
|
||||||
'crazydomain' => [
|
'crazydomain' => [
|
||||||
'api'=> \Dreamscape\API::class,
|
'api'=> \Dreamscape\API::class,
|
||||||
'registrar' => 'crazydomain', // Key in the domain_registrars table
|
'registrar' => 'crazydomain', // Key in the domain_registrars table
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
70
database/migrations/2023_05_10_215107_accounting_invoice.php
Normal file
70
database/migrations/2023_05_10_215107_accounting_invoice.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('invoice__provider', function (Blueprint $table) {
|
||||||
|
$table->timestamps();
|
||||||
|
$table->integer('invoice_id')->unsigned();
|
||||||
|
$table->integer('provider_oauth_id')->unsigned();
|
||||||
|
$table->integer('site_id')->unsigned();
|
||||||
|
$table->string('ref');
|
||||||
|
$table->integer('synctoken');
|
||||||
|
|
||||||
|
$table->foreign(['invoice_id','site_id'])->references(['id','site_id'])->on('invoices');
|
||||||
|
$table->foreign(['provider_oauth_id','site_id'])->references(['id','site_id'])->on('provider_oauth');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create('product__provider', function (Blueprint $table) {
|
||||||
|
$table->integer('product_id')->unsigned();
|
||||||
|
$table->integer('provider_oauth_id')->unsigned();
|
||||||
|
$table->integer('site_id')->unsigned();
|
||||||
|
$table->string('ref');
|
||||||
|
|
||||||
|
$table->foreign(['product_id','site_id'])->references(['id','site_id'])->on('products');
|
||||||
|
$table->foreign(['provider_oauth_id','site_id'])->references(['id','site_id'])->on('provider_oauth');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create('tax__provider', function (Blueprint $table) {
|
||||||
|
$table->timestamps();
|
||||||
|
$table->integer('tax_id')->unsigned();
|
||||||
|
$table->integer('provider_oauth_id')->unsigned();
|
||||||
|
$table->integer('site_id')->unsigned();
|
||||||
|
$table->string('ref');
|
||||||
|
$table->integer('synctoken');
|
||||||
|
|
||||||
|
$table->foreign(['tax_id'])->references(['id'])->on('taxes');
|
||||||
|
$table->foreign(['provider_oauth_id','site_id'])->references(['id','site_id'])->on('provider_oauth');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('products', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('accounting');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('tax__provider');
|
||||||
|
Schema::dropIfExists('product__provider');
|
||||||
|
Schema::dropIfExists('invoice__provider');
|
||||||
|
|
||||||
|
Schema::table('products', function (Blueprint $table) {
|
||||||
|
$table->string('accounting');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -105,15 +105,29 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- Accounting -->
|
<!-- Accounting -->
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
@include('adminlte::widget.form_select',[
|
<span class="h5">Accounting</span>
|
||||||
'label'=>'Accounting',
|
<hr>
|
||||||
'icon'=>'fas fa-calculator',
|
@foreach (\App\Models\ProviderOauth::providers() as $apo)
|
||||||
'id'=>'accounting',
|
<div class="form-group">
|
||||||
'old'=>'accounting',
|
<label for="{{ $x=sprintf('acc_%d',$apo->id) }}">{{ ucfirst($apo->name) }}</label>
|
||||||
'name'=>'accounting',
|
<div class="input-group has-validation">
|
||||||
'value'=>$o->accounting,
|
<div class="input-group-prepend">
|
||||||
'options'=>$o->accounting('intuit'),
|
<span class="input-group-text"><i class="fa-fw fas fa-calculator"></i></span>
|
||||||
])
|
</div>
|
||||||
|
<select class="form-control @error($x) is-invalid @enderror select" id="{{ $x }}" name="{{ sprintf('accounting[%d]',$apo->id) }}">
|
||||||
|
<option></option>
|
||||||
|
@foreach ($o->accounting($apo->name) as $v)
|
||||||
|
<option value="{{ $v['id'] }}" @if($o->provider_ref($apo) === (string)$v['id'])selected @endif>{{ $v['value'] }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
<span class="invalid-feedback" role="alert">
|
||||||
|
@error($x)
|
||||||
|
{{ $message }}
|
||||||
|
@enderror
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -121,6 +135,9 @@
|
|||||||
<div class="col-12 offset-md-3 col-md-5">
|
<div class="col-12 offset-md-3 col-md-5">
|
||||||
<span class="h5">Pricing</span><small> Ex Taxes</small>
|
<span class="h5">Pricing</span><small> Ex Taxes</small>
|
||||||
<hr>
|
<hr>
|
||||||
|
@error('pricing')
|
||||||
|
@include('adminlte::widget.errors')
|
||||||
|
@enderror
|
||||||
|
|
||||||
<ul class="nav nav-pills w-100 pl-0 pt-2 pb-2">
|
<ul class="nav nav-pills w-100 pl-0 pt-2 pb-2">
|
||||||
@foreach(\App\Models\Group::pricing()->active()->get() as $go)
|
@foreach(\App\Models\Group::pricing()->active()->get() as $go)
|
||||||
@ -158,7 +175,7 @@
|
|||||||
|
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="{{ $x=sprintf('setup_%d_%d',$bp,$go->id) }}">{{ $detail['name'] }} Reoccurring</label>
|
<label for="{{ $x=sprintf('setup_%d_%d',$bp,$go->id) }}">{{ $detail['name'] }} Setup</label>
|
||||||
<div class="input-group has-validation">
|
<div class="input-group has-validation">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text"><i class="fa-fw fas fa-cog"></i></span>
|
<span class="input-group-text"><i class="fa-fw fas fa-cog"></i></span>
|
||||||
@ -242,6 +259,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
$('#model_id').select2();
|
$('#model_id').select2();
|
||||||
|
$('.select').select2();
|
||||||
|
|
||||||
// After we render the page, hide the supplier_product if this product has no model.
|
// After we render the page, hide the supplier_product if this product has no model.
|
||||||
// We do this here, because adding d-none to the div results in the select2 input not presenting correctly
|
// We do this here, because adding d-none to the div results in the select2 input not presenting correctly
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Product</th>
|
<th>Product</th>
|
||||||
<td class="text-center" colspan="2">#{{ $c->supplied->id }}: {{ $c->supplied->name_long }}</td>
|
<td class="text-center" colspan="2"><a href="{{ url('a/product/details',$c->supplied->id) }}">#{{ $c->supplied->id }}: {{ $c->supplied->name_long }}</a></td>
|
||||||
@if ($p->exists)
|
@if ($p->exists)
|
||||||
<th> </th>
|
<th> </th>
|
||||||
<td class="text-center" colspan="2">#{{ $p->supplied->id }}: {{ $p->supplied->name_long }}</td>
|
<td class="text-center" colspan="2">#{{ $p->supplied->id }}: {{ $p->supplied->name_long }}</td>
|
||||||
|
Loading…
Reference in New Issue
Block a user