Merge pull request #10715 from turbo124/v5-develop
Minor fixes for test scenario
This commit is contained in:
commit
bf6653636a
|
|
@ -252,8 +252,13 @@ class BaseRule implements RuleInterface
|
|||
|
||||
$tax_data = $company->origin_tax_data;
|
||||
|
||||
} elseif($this->client->location && $this->client->location->is_shipping_location && $this->client->location->tax_data){
|
||||
|
||||
$tax_data = $this->client->location->tax_data;
|
||||
|
||||
} elseif ($this->client->tax_data) {
|
||||
|
||||
|
||||
$tax_data = $this->client->tax_data;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Jobs\Client;
|
||||
|
||||
use App\DataProviders\USStates;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\Location;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class UpdateLocationTaxData implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
use MakesHash;
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param Location $location
|
||||
* @param Company $company
|
||||
*/
|
||||
public function __construct(public Location $location, protected Company $company)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
if ($this->company->account->isFreeHostedClient() || $this->location->vendor || $this->location->country_id != 840) {
|
||||
return;
|
||||
}
|
||||
|
||||
$client = $this->location->client;
|
||||
|
||||
try {
|
||||
|
||||
if (!$this->location->state && $this->location->postal_code) {
|
||||
|
||||
$this->location->update(['state' => USStates::getState($this->location->postal_code)]);
|
||||
$this->location->refresh();
|
||||
}
|
||||
|
||||
$tax_provider = new \App\Services\Tax\Providers\TaxProvider($this->company, $this->location->client);
|
||||
|
||||
$location_address = [
|
||||
'address2' => $this->location->address2 ?? '',
|
||||
'address1' => $this->location->address1 ?? '',
|
||||
'city' => $this->location->city ?? '',
|
||||
'state' => $this->location->state ?? '',
|
||||
'postal_code' => $this->location->postal_code ?? '',
|
||||
'country' => $this->location->country()->exists() ? $this->location->country->name : $this->company->country()->name,
|
||||
];
|
||||
|
||||
$tax_provider->setBillingAddress($location_address)
|
||||
->setShippingAddress($location_address)
|
||||
->updateLocationTaxData($this->location);
|
||||
|
||||
|
||||
} catch (\Exception $e) {
|
||||
nlog("Exception:: UpdateTaxData::" . $e->getMessage());
|
||||
nlog("problem getting tax data => ".$e->getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function middleware()
|
||||
{
|
||||
return [new WithoutOverlapping($this->location->client->id.$this->company->company_key)];
|
||||
}
|
||||
|
||||
public function failed($exception)
|
||||
{
|
||||
nlog("UpdateLocationTaxData failed => ".$exception->getMessage());
|
||||
config(['queue.failed.driver' => null]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -59,15 +59,17 @@ class UpdateTaxData implements ShouldQueue
|
|||
|
||||
try {
|
||||
|
||||
$tax_provider->updateClientTaxData();
|
||||
|
||||
|
||||
if (!$this->client->state && $this->client->postal_code) {
|
||||
|
||||
|
||||
$this->client->update(['state' => USStates::getState($this->client->postal_code)]);
|
||||
// $this->client->saveQuietly();
|
||||
|
||||
$this->client->refresh();
|
||||
|
||||
}
|
||||
|
||||
$tax_provider->setBillingAddress($this->getBillingAddress())
|
||||
->setShippingAddress($this->getShippingAddress())
|
||||
->updateClientTaxData();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
nlog("Exception:: UpdateTaxData::" . $e->getMessage());
|
||||
|
|
@ -76,9 +78,41 @@ class UpdateTaxData implements ShouldQueue
|
|||
|
||||
}
|
||||
|
||||
private function getBillingAddress(): array
|
||||
{
|
||||
|
||||
return [
|
||||
'address2' => $this->client->address2,
|
||||
'address1' => $this->client->address1,
|
||||
'city' => $this->client->city,
|
||||
'state' => $this->client->state,
|
||||
'postal_code' => $this->client->postal_code,
|
||||
'country' => $this->client->country->name,
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
private function getShippingAddress(): array
|
||||
{
|
||||
if(strlen($this->client->shipping_address1 ?? '') < 3) {
|
||||
return $this->getBillingAddress();
|
||||
}
|
||||
|
||||
return
|
||||
[
|
||||
'address2' => $this->client->shipping_address2,
|
||||
'address1' => $this->client->shipping_address1,
|
||||
'city' => $this->client->shipping_city,
|
||||
'state' => $this->client->shipping_state,
|
||||
'postal_code' => $this->client->shipping_postal_code,
|
||||
'country' => $this->client->shipping_country()->exists() ? $this->client->shipping_country->name : $this->client->country->name,
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
public function middleware()
|
||||
{
|
||||
return [new WithoutOverlapping($this->client->id.$this->company->id)];
|
||||
return [new WithoutOverlapping($this->client->id.$this->company->company_key)];
|
||||
}
|
||||
|
||||
public function failed($exception)
|
||||
|
|
|
|||
|
|
@ -56,8 +56,8 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
|||
* @property int|null $deleted_at
|
||||
* @property-read mixed $hashed_id
|
||||
* @property-read \App\Models\User $user
|
||||
* @property-read \App\Models\Client $client
|
||||
* @property-read \App\Models\Vendor $vendor
|
||||
* @property-read \App\Models\Client|null $client
|
||||
* @property-read \App\Models\Vendor|null $vendor
|
||||
* @property-read \App\Models\Company $company
|
||||
* @property-read \App\Models\Country|null $country
|
||||
*
|
||||
|
|
@ -109,6 +109,10 @@ class Location extends BaseModel
|
|||
return $this->belongsTo(Client::class);
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
public function vendor()
|
||||
{
|
||||
return $this->belongsTo(Vendor::class);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Models\Location;
|
||||
use App\Jobs\Client\UpdateLocationTaxData;
|
||||
class LocationObserver
|
||||
{
|
||||
/**
|
||||
* Handle the location "created" event.
|
||||
*
|
||||
* @param Location $location
|
||||
* @return void
|
||||
*/
|
||||
public function created(Location $location)
|
||||
{
|
||||
if ($location->country_id == 840 && $location->company->calculate_taxes && !$location->company->account->isFreeHostedClient()) {
|
||||
UpdateLocationTaxData::dispatch($location, $location->company);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the location "updated" event.
|
||||
*
|
||||
* @param Location $location
|
||||
* @return void
|
||||
*/
|
||||
public function updated(Location $location)
|
||||
{
|
||||
|
||||
if ($location->getOriginal('postal_code') != $location->postal_code && $location->country_id == 840 && $location->company->calculate_taxes && !$location->company->account->isFreeHostedClient()) {
|
||||
UpdateLocationTaxData::dispatch($location, $location->company);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the location "deleted" event.
|
||||
*
|
||||
* @param Location $location
|
||||
* @return void
|
||||
*/
|
||||
public function deleted(Location $location)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the location "restored" event.
|
||||
*
|
||||
* @param Location $location
|
||||
* @return void
|
||||
*/
|
||||
public function restored(Location $location)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the location "force deleted" event.
|
||||
*
|
||||
* @param Location $location
|
||||
* @return void
|
||||
*/
|
||||
public function forceDeleted(Location $location)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ use App\Models\Invoice;
|
|||
use App\Models\Payment;
|
||||
use App\Models\Product;
|
||||
use App\Models\Project;
|
||||
use App\Models\Location;
|
||||
use App\Models\Proposal;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\Subscription;
|
||||
|
|
@ -51,6 +52,7 @@ use App\Events\Task\TaskWasUpdated;
|
|||
use App\Events\User\UserWasCreated;
|
||||
use App\Events\User\UserWasDeleted;
|
||||
use App\Events\User\UserWasUpdated;
|
||||
use App\Observers\LocationObserver;
|
||||
use App\Observers\ProposalObserver;
|
||||
use App\Events\Quote\QuoteWasViewed;
|
||||
use App\Events\Task\TaskWasArchived;
|
||||
|
|
@ -691,6 +693,7 @@ class EventServiceProvider extends ServiceProvider
|
|||
Credit::observe(CreditObserver::class);
|
||||
Expense::observe(ExpenseObserver::class);
|
||||
Invoice::observe(InvoiceObserver::class);
|
||||
Location::observe(LocationObserver::class);
|
||||
Payment::observe(PaymentObserver::class);
|
||||
Product::observe(ProductObserver::class);
|
||||
Project::observe(ProjectObserver::class);
|
||||
|
|
|
|||
|
|
@ -1135,25 +1135,25 @@ class Peppol extends AbstractService
|
|||
$id->schemeID = $this->resolveScheme(true);
|
||||
$party->EndpointID = $id;
|
||||
|
||||
|
||||
|
||||
$party->PartyName[] = $party_name;
|
||||
|
||||
$address = new Address();
|
||||
$address->CityName = $this->invoice->client->city;
|
||||
$address->StreetName = $this->invoice->client->address1;
|
||||
$locationData = $this->invoice->service()->location();
|
||||
|
||||
if (strlen($this->invoice->client->address2 ?? '') > 1) {
|
||||
$address->AdditionalStreetName = $this->invoice->client->address2;
|
||||
$address = new Address();
|
||||
$address->CityName = $locationData['city'];
|
||||
$address->StreetName = $locationData['address1'];
|
||||
|
||||
if (strlen($locationData['address2'] ?? '') > 1) {
|
||||
$address->AdditionalStreetName = $locationData['address2'];
|
||||
}
|
||||
|
||||
$address->PostalZone = $this->invoice->client->postal_code;
|
||||
$address->PostalZone = $locationData['postal_code'];
|
||||
// $address->CountrySubentity = $this->invoice->client->state;
|
||||
|
||||
$country = new Country();
|
||||
|
||||
$ic = new IdentificationCode();
|
||||
$ic->value = substr($this->invoice->client->country->iso_3166_2, 0, 2);
|
||||
$ic->value = substr($locationData['country_code'], 0, 2);
|
||||
|
||||
$country->IdentificationCode = $ic;
|
||||
$address->Country = $country;
|
||||
|
|
@ -1180,24 +1180,28 @@ class Peppol extends AbstractService
|
|||
|
||||
private function getDelivery(): array
|
||||
{
|
||||
$locationData = $this->invoice->service()->location();
|
||||
$delivery = new \InvoiceNinja\EInvoice\Models\Peppol\DeliveryType\Delivery();
|
||||
$location = new \InvoiceNinja\EInvoice\Models\Peppol\LocationType\DeliveryLocation();
|
||||
|
||||
$address = new Address();
|
||||
// $address->CityName = $this->invoice->client->city;
|
||||
// $address->StreetName = $this->invoice->client->address1;
|
||||
$address->CityName = $locationData['shipping_city'];
|
||||
$address->StreetName = $locationData['shipping_address1'];
|
||||
|
||||
// if (strlen($this->invoice->client->address2 ?? '') > 1) {
|
||||
// $address->AdditionalStreetName = $this->invoice->client->address2;
|
||||
// }
|
||||
|
||||
// $address->PostalZone = $this->invoice->client->postal_code;
|
||||
// $address->CountrySubentity = $this->invoice->client->state;
|
||||
if (strlen($locationData['shipping_address2'] ?? '') > 1) {
|
||||
$address->AdditionalStreetName = $locationData['shipping_address2'];
|
||||
}
|
||||
|
||||
$address->PostalZone = $locationData['shipping_postal_code'];
|
||||
|
||||
$country = new Country();
|
||||
|
||||
$ic = new IdentificationCode();
|
||||
$shipping = $this->invoice->client->shipping_country ? $this->invoice->client->shipping_country->iso_3166_2 : $this->invoice->client->country->iso_3166_2;
|
||||
$ic->value = substr($locationData['shipping_country_code'], 0, 2);
|
||||
$country->IdentificationCode = $ic;
|
||||
|
||||
$ic = new IdentificationCode();
|
||||
$shipping = $locationData['shipping_country_code'];
|
||||
$ic->value = $shipping;
|
||||
|
||||
$country->IdentificationCode = $ic;
|
||||
|
|
|
|||
|
|
@ -171,7 +171,6 @@ class LocationData extends AbstractService
|
|||
|
||||
return $str;
|
||||
|
||||
|
||||
}
|
||||
|
||||
private function getBusinessAddress1(): string
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ namespace App\Services\Tax\Providers;
|
|||
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\Location;
|
||||
|
||||
class TaxProvider
|
||||
{
|
||||
|
|
@ -52,6 +53,10 @@ class TaxProvider
|
|||
|
||||
private bool $updated_client = false;
|
||||
|
||||
private array $billing_address = [];
|
||||
|
||||
private array $shipping_address = [];
|
||||
|
||||
public function __construct(public Company $company, public ?Client $client = null)
|
||||
{
|
||||
}
|
||||
|
|
@ -117,25 +122,7 @@ class TaxProvider
|
|||
{
|
||||
$this->configureProvider($this->provider, $this->client->country->iso_3166_2); //hard coded for now to one provider, but we'll be able to swap these out later
|
||||
|
||||
$billing_details = [
|
||||
'address2' => $this->client->address2,
|
||||
'address1' => $this->client->address1,
|
||||
'city' => $this->client->city,
|
||||
'state' => $this->client->state,
|
||||
'postal_code' => $this->client->postal_code,
|
||||
'country' => $this->client->country->name,
|
||||
];
|
||||
|
||||
$shipping_details = [
|
||||
'address2' => $this->client->shipping_address2,
|
||||
'address1' => $this->client->shipping_address1,
|
||||
'city' => $this->client->shipping_city,
|
||||
'state' => $this->client->shipping_state,
|
||||
'postal_code' => $this->client->shipping_postal_code,
|
||||
'country' => $this->client->shipping_country()->exists() ? $this->client->shipping_country->name : $this->client->country->name,
|
||||
];
|
||||
|
||||
$taxable_address = $this->taxShippingAddress() ? $shipping_details : $billing_details;
|
||||
$taxable_address = $this->taxShippingAddress() ? $this->getShippingAddress() : $this->getBillingAddress();
|
||||
|
||||
$tax_provider = new $this->provider($taxable_address);
|
||||
|
||||
|
|
@ -155,6 +142,23 @@ class TaxProvider
|
|||
|
||||
}
|
||||
|
||||
public function updateLocationTaxData(Location $location): self
|
||||
{
|
||||
$this->configureProvider($this->provider, $location->country->iso_3166_2);
|
||||
|
||||
$tax_provider = new $this->provider($this->getBillingAddress());
|
||||
|
||||
$tax_provider->setApiCredentials($this->api_credentials);
|
||||
|
||||
$tax_data = $tax_provider->run();
|
||||
|
||||
if ($tax_data) {
|
||||
$location->tax_data = $tax_data;
|
||||
$location->saveQuietly();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* taxShippingAddress
|
||||
*
|
||||
|
|
@ -262,4 +266,28 @@ class TaxProvider
|
|||
|
||||
}
|
||||
|
||||
public function setBillingAddress(array $address): self
|
||||
{
|
||||
$this->billing_address = $address;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setShippingAddress(array $address): self
|
||||
{
|
||||
$this->shipping_address = $address;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBillingAddress(): array
|
||||
{
|
||||
return $this->billing_address;
|
||||
}
|
||||
|
||||
public function getShippingAddress(): array
|
||||
{
|
||||
return $this->shipping_address;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class LocationFactory extends Factory
|
|||
'city' => $this->faker->city(),
|
||||
'state' => $this->faker->state(),
|
||||
'postal_code' => $this->faker->postcode(),
|
||||
'country_id' => 4,
|
||||
'country_id' => 840,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5510,6 +5510,9 @@ $lang = array(
|
|||
'disable_emails' => 'Disable Emails',
|
||||
'disable_emails_error' => 'You are not authorized to send emails',
|
||||
'disable_emails_help' => 'Prevents a user from sending emails from the system',
|
||||
'add_location' => 'Add Location',
|
||||
'updated_location' => 'Updated Location',
|
||||
'created_location' => 'Created Location',
|
||||
);
|
||||
|
||||
return $lang;
|
||||
|
|
|
|||
|
|
@ -302,6 +302,23 @@ class LocationApiTest extends TestCase
|
|||
'X-API-TOKEN' => $this->token,
|
||||
])->postJson('/api/v1/locations', $data);
|
||||
|
||||
$response->assertStatus(422);
|
||||
|
||||
$data = [
|
||||
'name' => 'Test Location',
|
||||
'address1' => '123 Test St',
|
||||
'address2' => 'Suite 100',
|
||||
'city' => 'Test City',
|
||||
'state' => 'TS',
|
||||
'postal_code' => '12345',
|
||||
'country_id' => '840', // USA
|
||||
'client_id' => $this->client->id,
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->postJson('/api/v1/locations', $data);
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$arr = $response->json();
|
||||
|
|
@ -314,6 +331,7 @@ class LocationApiTest extends TestCase
|
|||
$location = Location::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'country_id' => '840',
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders([
|
||||
|
|
@ -331,11 +349,13 @@ class LocationApiTest extends TestCase
|
|||
$location = Location::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'client_id' => $this->client->id,
|
||||
]);
|
||||
|
||||
$data = [
|
||||
'name' => 'Updated Location',
|
||||
'address1' => '456 Update St',
|
||||
'client_id' => $this->client->id,
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
|
|
|
|||
Loading…
Reference in New Issue