Updates for Quickbooks imports
This commit is contained in:
parent
a12a025b28
commit
ced4f771ee
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Casts;
|
||||
|
||||
use App\DataMapper\PaymentSync;
|
||||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||
|
||||
class PaymentSyncCast implements CastsAttributes
|
||||
{
|
||||
public function get($model, string $key, $value, array $attributes)
|
||||
{
|
||||
|
||||
if (is_null($value)) {
|
||||
return null; // Return null if the value is null
|
||||
}
|
||||
|
||||
$data = json_decode($value, true);
|
||||
|
||||
if (!is_array($data)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$is = new PaymentSync();
|
||||
$is->qb_id = $data['qb_id'];
|
||||
|
||||
return $is;
|
||||
}
|
||||
|
||||
public function set($model, string $key, $value, array $attributes)
|
||||
{
|
||||
return [
|
||||
$key => json_encode([
|
||||
'qb_id' => $value->qb_id,
|
||||
])
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\DataMapper;
|
||||
|
||||
use App\Casts\PaymentSyncCast;
|
||||
use Illuminate\Contracts\Database\Eloquent\Castable;
|
||||
|
||||
/**
|
||||
* PaymentSync.
|
||||
*/
|
||||
class PaymentSync implements Castable
|
||||
{
|
||||
public string $qb_id;
|
||||
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
$this->qb_id = $attributes['qb_id'] ?? '';
|
||||
}
|
||||
/**
|
||||
* Get the name of the caster class to use when casting from / to this cast target.
|
||||
*
|
||||
* @param array<string, mixed> $arguments
|
||||
*/
|
||||
public static function castUsing(array $arguments): string
|
||||
{
|
||||
return PaymentSyncCast::class;
|
||||
}
|
||||
|
||||
public static function fromArray(array $data): self
|
||||
{
|
||||
return new self($data);
|
||||
}
|
||||
}
|
||||
|
|
@ -11,15 +11,16 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Events\Payment\PaymentWasRefunded;
|
||||
use App\Events\Payment\PaymentWasVoided;
|
||||
use App\Services\Ledger\LedgerService;
|
||||
use App\Services\Payment\PaymentService;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Number;
|
||||
use App\Utils\Traits\Inviteable;
|
||||
use App\DataMapper\PaymentSync;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\Inviteable;
|
||||
use App\Services\Ledger\LedgerService;
|
||||
use App\Events\Payment\PaymentWasVoided;
|
||||
use App\Services\Payment\PaymentService;
|
||||
use App\Utils\Traits\Payment\Refundable;
|
||||
use App\Events\Payment\PaymentWasRefunded;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
/**
|
||||
|
|
@ -64,6 +65,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
|||
* @property string|null $custom_value4
|
||||
* @property int|null $transaction_id
|
||||
* @property string|null $idempotency_key
|
||||
* @property object|null $sync
|
||||
* @property-read \App\Models\User|null $assigned_user
|
||||
* @property-read \App\Models\Client $client
|
||||
* @property-read \App\Models\Company $company
|
||||
|
|
@ -168,6 +170,7 @@ class Payment extends BaseModel
|
|||
'is_deleted' => 'bool',
|
||||
'meta' => 'object',
|
||||
'refund_meta' => 'array',
|
||||
'sync' => PaymentSync::class,
|
||||
];
|
||||
|
||||
protected $with = [
|
||||
|
|
|
|||
|
|
@ -44,25 +44,7 @@ class QbPayment implements SyncInterface
|
|||
$ninja_payment = $payment_transformer->buildPayment($payment);
|
||||
$ninja_payment->service()->applyNumber()->save();
|
||||
|
||||
$invoice = Invoice::query()
|
||||
->withTrashed()
|
||||
->where('company_id', $this->service->company->id)
|
||||
->where('sync->qb_id', $payment['invoice_id'])
|
||||
->first();
|
||||
|
||||
if ($invoice) {
|
||||
|
||||
$paymentable = new \App\Models\Paymentable();
|
||||
$paymentable->payment_id = $ninja_payment->id;
|
||||
$paymentable->paymentable_id = $invoice->id;
|
||||
$paymentable->paymentable_type = 'invoices';
|
||||
$paymentable->amount = $transformed['applied'] + $ninja_payment->credits->sum('amount');
|
||||
$paymentable->created_at = $ninja_payment->date; //@phpstan-ignore-line
|
||||
$paymentable->save();
|
||||
|
||||
$invoice->service()->applyPayment($ninja_payment, $paymentable->amount);
|
||||
|
||||
}
|
||||
$payment_transformer->associatePaymentToInvoice($ninja_payment, $payment);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use App\Models\Company;
|
|||
use App\Models\Invoice;
|
||||
use App\Models\Product;
|
||||
use App\DataMapper\InvoiceItem;
|
||||
use App\Models\TaxRate;
|
||||
|
||||
/**
|
||||
* Class InvoiceTransformer.
|
||||
|
|
@ -52,6 +53,7 @@ class InvoiceTransformer extends BaseTransformer
|
|||
'tax_rate1' => $rate = $this->calculateTotalTax($qb_data),
|
||||
'tax_name1' => $rate > 0 ? "Sales Tax" : "",
|
||||
'custom_surcharge1' => $this->checkIfDiscountAfterTax($qb_data),
|
||||
'balance' => data_get($qb_data, 'Balance', 0),
|
||||
|
||||
] : false;
|
||||
}
|
||||
|
|
@ -81,19 +83,49 @@ class InvoiceTransformer extends BaseTransformer
|
|||
|
||||
private function calculateTotalTax($qb_data)
|
||||
{
|
||||
$taxLines = data_get($qb_data, 'TxnTaxDetail.TaxLine', []);
|
||||
$total_tax = data_get($qb_data,'TxnTaxDetail.TotalTax', false);
|
||||
|
||||
if (!is_array($taxLines)) {
|
||||
if($total_tax == "0") {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$taxLines = data_get($qb_data, 'TxnTaxDetail.TaxLine', []) ?? [];
|
||||
|
||||
if (!empty($taxLines) && !isset($taxLines[0])) {
|
||||
$taxLines = [$taxLines];
|
||||
}
|
||||
|
||||
$totalTaxRate = 0;
|
||||
|
||||
nlog($taxLines);
|
||||
|
||||
foreach ($taxLines as $taxLine) {
|
||||
$taxRate = data_get($taxLine, 'TaxLineDetail.TaxPercent', 0);
|
||||
$totalTaxRate += $taxRate;
|
||||
}
|
||||
|
||||
|
||||
if ($totalTaxRate > 0) {
|
||||
$formattedTaxRate = rtrim(rtrim(number_format($totalTaxRate, 6), '0'), '.');
|
||||
$formattedTaxRate = trim($formattedTaxRate);
|
||||
|
||||
$tr = \App\Models\TaxRate::firstOrNew(
|
||||
[
|
||||
'company_id' => $this->company->id,
|
||||
'rate' => $formattedTaxRate,
|
||||
],
|
||||
[
|
||||
'name' => "Sales Tax [{$formattedTaxRate}]",
|
||||
'rate' => $formattedTaxRate,
|
||||
]
|
||||
);
|
||||
$tr->company_id = $this->company->id;
|
||||
$tr->user_id = $this->company->owner()->id;
|
||||
$tr->save();
|
||||
}
|
||||
// ... exi
|
||||
|
||||
|
||||
return (float)$totalTaxRate;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,12 @@
|
|||
|
||||
namespace App\Services\Quickbooks\Transformers;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\Payment;
|
||||
use App\Factory\PaymentFactory;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Company;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\DataMapper\PaymentSync;
|
||||
use App\Factory\PaymentFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -35,6 +37,7 @@ class PaymentTransformer extends BaseTransformer
|
|||
{
|
||||
|
||||
return [
|
||||
'id' => data_get($qb_data, 'Id', null),
|
||||
'date' => data_get($qb_data, 'TxnDate', now()->format('Y-m-d')),
|
||||
'amount' => floatval(data_get($qb_data, 'TotalAmt', 0)),
|
||||
'applied' => data_get($qb_data, 'TotalAmt', 0) - data_get($qb_data, 'UnappliedAmt', 0),
|
||||
|
|
@ -45,15 +48,72 @@ class PaymentTransformer extends BaseTransformer
|
|||
];
|
||||
}
|
||||
|
||||
public function associatePaymentToInvoice(Payment $payment, mixed $qb_data)
|
||||
{
|
||||
|
||||
$invoice = Invoice::query()
|
||||
->withTrashed()
|
||||
->where('company_id', $this->company->id)
|
||||
->where('sync->qb_id', data_get($qb_data, 'invoice_id'))
|
||||
->first();
|
||||
|
||||
if(!$invoice)
|
||||
return;
|
||||
|
||||
$lines = data_get($qb_data, 'Line', []) ?? [];
|
||||
|
||||
if(!empty($lines) && !isset($lines[0])) {
|
||||
$lines = [$lines];
|
||||
}
|
||||
|
||||
foreach($lines as $item) {
|
||||
$id = data_get($item, 'LinkedTxn.TxnId', false);
|
||||
$tx_type = data_get($item, 'LinkedTxn.TxnType', false);
|
||||
$amount = data_get($item, 'Amount', 0);
|
||||
|
||||
if($tx_type == 'Invoice' && $id == $invoice->sync->qb_id) {
|
||||
|
||||
$paymentable = new \App\Models\Paymentable();
|
||||
$paymentable->payment_id = $payment->id;
|
||||
$paymentable->paymentable_id = $invoice->id;
|
||||
$paymentable->paymentable_type = 'invoices';
|
||||
$paymentable->amount = $related_invoice['amount'];
|
||||
$paymentable->created_at = $payment->date; //@phpstan-ignore-line
|
||||
$paymentable->save();
|
||||
|
||||
$invoice->service()->applyPayment($payment, $paymentable->amount);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function buildPayment($qb_data): ?Payment
|
||||
{
|
||||
$ninja_payment_data = $this->transform($qb_data);
|
||||
|
||||
$search_payment = Payment::query()
|
||||
->withTrashed()
|
||||
->where('company_id', $this->company->id)
|
||||
->where('sync->qb_id', $ninja_payment_data['id'])
|
||||
->first();
|
||||
|
||||
if($search_payment) {
|
||||
return $search_payment;
|
||||
}
|
||||
|
||||
|
||||
if ($ninja_payment_data['client_id']) {
|
||||
$payment = PaymentFactory::create($this->company->id, $this->company->owner()->id, $ninja_payment_data['client_id']);
|
||||
$payment->amount = $ninja_payment_data['amount'];
|
||||
$payment->applied = $ninja_payment_data['applied'];
|
||||
$payment->status_id = 4;
|
||||
|
||||
$sync = new PaymentSync();
|
||||
$sync->qb_id = $ninja_payment_data['id'];
|
||||
$payment->sync = $sync;
|
||||
|
||||
$payment->fill($ninja_payment_data);
|
||||
$payment->save();
|
||||
|
||||
|
|
@ -77,7 +137,9 @@ class PaymentTransformer extends BaseTransformer
|
|||
{
|
||||
$credit_line = null;
|
||||
|
||||
foreach ($qb_data->Line as $item) {
|
||||
$credit_array = data_get($qb_data, 'Line', []);
|
||||
|
||||
foreach ($credit_array as $item) {
|
||||
|
||||
if (data_get($item, 'LinkedTxn.TxnType', null) == 'CreditMemo') {
|
||||
$credit_line = $item;
|
||||
|
|
@ -95,14 +157,14 @@ class PaymentTransformer extends BaseTransformer
|
|||
|
||||
$line = new \App\DataMapper\InvoiceItem();
|
||||
$line->quantity = 1;
|
||||
$line->cost = $credit_line->Amount;
|
||||
$line->cost = data_get($credit_line, 'Amount', 0);
|
||||
$line->product_key = 'CREDITMEMO';
|
||||
$line->notes = $payment->private_notes;
|
||||
|
||||
$credit->date = $qb_data->TxnDate;
|
||||
$credit->date = data_get($qb_data, 'TxnDate', now()->format('Y-m-d'));
|
||||
$credit->status_id = 4;
|
||||
$credit->amount = $credit_line->Amount;
|
||||
$credit->paid_to_date = $credit_line->Amount;
|
||||
$credit->amount = data_get($credit_line, 'Amount', 0);
|
||||
$credit->paid_to_date = data_get($credit_line, 'Amount', 0);
|
||||
$credit->balance = 0;
|
||||
$credit->line_items = [$line];
|
||||
$credit->save();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
|
||||
Schema::table('payments', function (Blueprint $table) {
|
||||
$table->text('sync')->nullable();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue