From 17ebbb71e8eb2e9b0712b5353a81dfc54a669200 Mon Sep 17 00:00:00 2001 From: Deon George Date: Wed, 10 May 2023 12:59:42 +0900 Subject: [PATCH] Integration with Intuit - get accounting details for products --- app/Console/Commands/AccountingItemList.php | 71 +++++++++++++++++++ app/Http/Controllers/AccountingController.php | 48 +++++++++++++ app/Http/Controllers/WelcomeController.php | 5 -- app/Http/Requests/ProductAddEdit.php | 4 +- app/Models/Product.php | 6 ++ app/Models/ProviderOauth.php | 11 ++- .../adminlte/product/widget/detail.blade.php | 7 +- routes/api.php | 12 ++-- 8 files changed, 147 insertions(+), 17 deletions(-) create mode 100644 app/Console/Commands/AccountingItemList.php create mode 100644 app/Http/Controllers/AccountingController.php diff --git a/app/Console/Commands/AccountingItemList.php b/app/Console/Commands/AccountingItemList.php new file mode 100644 index 0000000..e58407b --- /dev/null +++ b/app/Console/Commands/AccountingItemList.php @@ -0,0 +1,71 @@ +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)); + } + } +} \ No newline at end of file diff --git a/app/Http/Controllers/AccountingController.php b/app/Http/Controllers/AccountingController.php new file mode 100644 index 0000000..5941b57 --- /dev/null +++ b/app/Http/Controllers/AccountingController.php @@ -0,0 +1,48 @@ +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 + { + $so = ProviderOauth::where('name',$provider)->singleOrFail(); + // @todo This should be a variable + $uo = User::findOrFail(1); + + if (($x=$so->tokens->where('user_id',$uo->id))->count() !== 1) + abort(500,sprintf('Unknown Tokens for [%s]',$uo->email)); + + $to = $x->pop(); + + $api = $so->API($to,TRUE); // @todo Remove TRUE + + return $api->getItems() + ->pluck('pid','FullyQualifiedName') + ->transform(function($item,$value) { return ['id'=>$item,'value'=>$value]; }) + ->values(); + } + + public function webhook(Request $request) + { + Log::channel('webhook')->debug('Webhook event',['request'=>$request]); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/WelcomeController.php b/app/Http/Controllers/WelcomeController.php index d02d30c..3e4cf32 100644 --- a/app/Http/Controllers/WelcomeController.php +++ b/app/Http/Controllers/WelcomeController.php @@ -10,9 +10,4 @@ class WelcomeController extends Controller public function home() { return view('welcome.home'); } - - public function webhook(Request $request) - { - Log::channel('webhook')->debug('Webhook event',['request'=>$request]); - } } \ No newline at end of file diff --git a/app/Http/Requests/ProductAddEdit.php b/app/Http/Requests/ProductAddEdit.php index da60a24..98846ca 100644 --- a/app/Http/Requests/ProductAddEdit.php +++ b/app/Http/Requests/ProductAddEdit.php @@ -30,11 +30,11 @@ class ProductAddEdit extends FormRequest 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:255', + '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|string', + 'accounting' => 'nullable|string', // @todo Validate that the value is in the accounting system 'pricing' => 'required|array', // @todo Validate the elements in the pricing ]; } diff --git a/app/Models/Product.php b/app/Models/Product.php index 3959f94..44cca1f 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -14,6 +14,7 @@ use Illuminate\Support\Facades\File; use Illuminate\Support\Str; use Leenooks\Traits\ScopeActive; +use App\Http\Controllers\AccountingController; use App\Interfaces\{IDs,ProductItem}; use App\Traits\{ProductDetails,SiteID}; @@ -358,6 +359,11 @@ class Product extends Model implements IDs return round($price,2); } + public function accounting(string $provider): Collection + { + return AccountingController::list($provider); + } + /** * Return the charge from the pricing table for the specific time period and group * diff --git a/app/Models/ProviderOauth.php b/app/Models/ProviderOauth.php index 356e305..f1be11a 100644 --- a/app/Models/ProviderOauth.php +++ b/app/Models/ProviderOauth.php @@ -18,8 +18,15 @@ class ProviderOauth extends Model public function accounts() { - return $this->belongsToMany(Account::class,'account_provider') - ->where('account_provider.site_id',$this->site_id) + return $this->belongsToMany(Account::class,'account__provider') + ->where('account__provider.site_id',$this->site_id) + ->withPivot('ref','synctoken','created_at','updated_at'); + } + + public function products() + { + return $this->belongsToMany(Product::class,'product__provider') + ->where('product__provider.site_id',$this->site_id) ->withPivot('ref','synctoken','created_at','updated_at'); } diff --git a/resources/views/theme/backend/adminlte/product/widget/detail.blade.php b/resources/views/theme/backend/adminlte/product/widget/detail.blade.php index dfe9680..fb33b9b 100644 --- a/resources/views/theme/backend/adminlte/product/widget/detail.blade.php +++ b/resources/views/theme/backend/adminlte/product/widget/detail.blade.php @@ -105,13 +105,14 @@
- @include('adminlte::widget.form_text',[ + @include('adminlte::widget.form_select',[ 'label'=>'Accounting', 'icon'=>'fas fa-calculator', 'id'=>'accounting', 'old'=>'accounting', 'name'=>'accounting', - 'value'=>$o->accounting ?? '', + 'value'=>$o->accounting, + 'options'=>$o->accounting('intuit'), ])
@@ -256,7 +257,7 @@ // Find the setup input and toggle it input = $('#'+input.attr('id').replace('base','setup')+''); input.prop('disabled',(i,v)=>!v); - }) + }); }); @append \ No newline at end of file diff --git a/routes/api.php b/routes/api.php index e0f8f21..b02c2ee 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,10 +1,10 @@ ['auth:api','role:reseller']], function() { Route::group(['middleware'=>'auth:api'], function() { Route::post('/u/checkout/fee/{o}',[CheckoutController::class,'fee']) ->where('o','[0-9]+'); + + Route::any('/intuit/accounting/list',[AccountingController::class,'list']); }); -Route::any('/intuit/webhook',[WelcomeController::class,'webhook']) - ->where('o','[0-9]+'); \ No newline at end of file +// @todo Take the specific 'intuit' out of this url, to enable other accounting methods +Route::any('/intuit/webhook',[AccountingController::class,'webhook']); \ No newline at end of file