<?php namespace App\Console\Commands\Intuit; use Illuminate\Console\Command; use Intuit\Jobs\AccountingInvoiceUpdate; use Intuit\Models\Invoice as AccInvoice; use Intuit\Traits\ProviderTokenTrait; use App\Models\Invoice; class InvoiceAdd extends Command { use ProviderTokenTrait; /** * The name and signature of the console command. * * @var string */ protected $signature = 'intuit:invoice:add' .' {id : Invoice ID}' .' {user? : User Email}'; /** * The console command description. * * @var string */ protected $description = 'Add an invoice to the accounting provider'; /** * Execute the console command. * * @return int * @throws \Exception */ public function handle() { $to = $this->providerToken($this->argument('user')); $io = Invoice::findOrFail($this->argument('id')); // Check the customer exists if ($io->account->providers->where('pivot.provider_oauth_id',$to->provider->id)->count() !== 1) throw new \Exception(sprintf('Account [%d] for Invoice [%d] not defined',$io->account_id,$io->id)); $ao = $io->account->providers->where('pivot.provider_oauth_id',$to->provider->id)->pop(); // Some validation if (! $ao->pivot->ref) { $this->error(sprintf('Accounting not defined for account [%d]',$io->account_id)); return self::FAILURE; } $acc = new AccInvoice; $acc->CustomerRef = (object)['value'=>$ao->pivot->ref]; $acc->DocNumber = $io->lid; $acc->TxnDate = $io->created_at->format('Y-m-d'); $acc->DueDate = $io->due_at->format('Y-m-d'); $lines = collect(); $c = 0; $subtotal = 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 ($io->items->groupBy( fn($item)=> sprintf('%s.%s.%s.%s', $item->item_type_name, $item->price_base, $item->product->provider_ref($to->provider), $item->taxes->pluck('description')->join('|'))) as $os) { $key = $os->first(); // Some validation if (! ($ref=$key->product->provider_ref($to->provider))) { $this->error(sprintf('Accounting not defined in product [%d]',$key->product_id)); return self::FAILURE; } if ($key->taxes->count() !== 1) { $this->error(sprintf('Cannot handle when there is not just 1 tax line [%d]',$key->id)); return self::FAILURE; } $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'=>$tcf=$key->taxes->first()->tax->provider_ref($to->provider)], ]; $line->Amount = round($os->sum('quantity')*$key->price_base,2); $subtotal += $line->Amount; $lines->push($line); } $acc->Line = $lines; // If our subtotal doesnt match, we need to add a tax line if ($io->subtotal !== $subtotal) { $acc->TxnTaxDetail = (object)[ 'TotalTax' => $x=$io->total-$subtotal, 'TaxLine' => [ (object) [ 'Amount' => $x, 'DetailType' => 'TaxLineDetail', 'TaxLineDetail' => (object)[ // @todo It is assumed there is only 1 tax category 'TaxRateRef' => (object)['value'=>$to->API()->getTaxCodeQuery($tcf)->getTaxRateRef()->first()], 'NetAmountTaxable' => $io->subtotal, ] ] ] ]; } return AccountingInvoiceUpdate::dispatchSync($to,$acc); } }