mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
commit
5796b9fd31
@ -15,6 +15,7 @@ use App\Models\Client;
|
|||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Models\Product;
|
use App\Models\Product;
|
||||||
use App\DataMapper\Tax\ZipTax\Response;
|
use App\DataMapper\Tax\ZipTax\Response;
|
||||||
|
use App\DataProviders\USStates;
|
||||||
|
|
||||||
class BaseRule implements RuleInterface
|
class BaseRule implements RuleInterface
|
||||||
{
|
{
|
||||||
@ -116,7 +117,7 @@ class BaseRule implements RuleInterface
|
|||||||
|
|
||||||
protected ?Client $client;
|
protected ?Client $client;
|
||||||
|
|
||||||
protected ?Response $tax_data;
|
public ?Response $tax_data;
|
||||||
|
|
||||||
public mixed $invoice;
|
public mixed $invoice;
|
||||||
|
|
||||||
@ -129,29 +130,42 @@ class BaseRule implements RuleInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setInvoice(mixed $invoice): self
|
public function setEntity(mixed $invoice): self
|
||||||
{
|
{
|
||||||
$this->invoice = $invoice;
|
$this->invoice = $invoice;
|
||||||
|
|
||||||
$this->configTaxData();
|
|
||||||
|
|
||||||
$this->client = $invoice->client;
|
$this->client = $invoice->client;
|
||||||
|
|
||||||
|
$this->configTaxData()
|
||||||
|
->resolveRegions();
|
||||||
|
|
||||||
$this->tax_data = new Response($this->invoice->tax_data);
|
$this->tax_data = new Response($this->invoice->tax_data);
|
||||||
|
|
||||||
$this->resolveRegions();
|
|
||||||
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function configTaxData(): self
|
private function configTaxData(): self
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if(!array_key_exists($this->client->country->iso_3166_2, $this->region_codes)) {
|
||||||
|
throw new \Exception('Automatic tax calculations not supported for this country');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->client_region = $this->region_codes[$this->client->country->iso_3166_2];
|
||||||
|
|
||||||
if($this->invoice->tax_data && $this->invoice->status_id > 1)
|
if($this->invoice->tax_data && $this->invoice->status_id > 1)
|
||||||
return $this;
|
return $this;
|
||||||
|
|
||||||
//determine if we are taxing locally or if we are taxing globally
|
//determine if we are taxing locally or if we are taxing globally
|
||||||
// $this->invoice->tax_data = $this->invoice->client->tax_data;
|
$this->invoice->tax_data = $this->invoice->client->tax_data ?: new Response([]);
|
||||||
|
|
||||||
|
if(strlen($this->invoice->tax_data?->originDestination) == 0 && $this->client->company->tax_data->seller_subregion != $this->client_subregion) {
|
||||||
|
$tax_data = $this->invoice->tax_data;
|
||||||
|
$tax_data->originDestination = "D";
|
||||||
|
$tax_data->geoState = $this->client_subregion;
|
||||||
|
$this->invoice->tax_data = $tax_data;
|
||||||
|
$this->invoice->saveQuietly();
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -160,20 +174,25 @@ class BaseRule implements RuleInterface
|
|||||||
private function resolveRegions(): self
|
private function resolveRegions(): self
|
||||||
{
|
{
|
||||||
|
|
||||||
if(!array_key_exists($this->client->country->iso_3166_2, $this->region_codes))
|
|
||||||
throw new \Exception('Automatic tax calculations not supported for this country');
|
|
||||||
|
|
||||||
$this->client_region = $this->region_codes[$this->client->country->iso_3166_2];
|
|
||||||
|
|
||||||
match($this->client_region){
|
match($this->client_region){
|
||||||
'US' => $this->client_subregion = $this->tax_data->geoState,
|
'US' => $this->client_subregion = strlen($this->invoice?->tax_data?->geoState) > 1 ? $this->invoice?->tax_data?->geoState : $this->getUSState(),
|
||||||
'EU' => $this->client_subregion = $this->client->country->iso_3166_2,
|
'EU' => $this->client_subregion = $this->client->country->iso_3166_2,
|
||||||
|
'AU' => $this->client_subregion = 'AU',
|
||||||
default => $this->client_subregion = $this->client->country->iso_3166_2,
|
default => $this->client_subregion = $this->client->country->iso_3166_2,
|
||||||
};
|
};
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getUSState(): string
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return USStates::getState(strlen($this->client->postal_code) > 1 ? $this->client->postal_code : $this->client->shipping_postal_code);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return $this->client->company->country()->iso_3166_2 == 'US' ? $this->client->company->tax_data->seller_subregion : 'CA';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function isTaxableRegion(): bool
|
public function isTaxableRegion(): bool
|
||||||
{
|
{
|
||||||
return $this->client->company->tax_data->regions->{$this->client_region}->tax_all_subregions || $this->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->apply_tax;
|
return $this->client->company->tax_data->regions->{$this->client_region}->tax_all_subregions || $this->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->apply_tax;
|
||||||
|
@ -144,9 +144,28 @@ class Rule extends BaseRule implements RuleInterface
|
|||||||
public function default(): self
|
public function default(): self
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if($this->tax_data?->stateSalesTax == 0) {
|
||||||
|
|
||||||
|
if($this->tax_data->originDestination == "O"){
|
||||||
|
$tax_region = $this->client->company->tax_data->seller_subregion;
|
||||||
|
$this->tax_rate1 = $this->invoice->client->company->tax_data->regions->US->subregions->{$tax_region}->tax_rate;
|
||||||
|
$this->tax_name1 = "{$this->tax_data->geoState} Sales Tax";
|
||||||
|
} else {
|
||||||
|
$this->tax_rate1 = $this->invoice->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->tax_rate;
|
||||||
|
$this->tax_name1 = $this->invoice->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->tax_name;
|
||||||
|
|
||||||
|
if($this->client_region == 'US')
|
||||||
|
$this->tax_name1 = "{$this->client_subregion} ".$this->tax_name1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
$this->tax_rate1 = $this->tax_data->taxSales * 100;
|
$this->tax_rate1 = $this->tax_data->taxSales * 100;
|
||||||
$this->tax_name1 = "{$this->tax_data->geoState} Sales Tax";
|
$this->tax_name1 = "{$this->tax_data->geoState} Sales Tax";
|
||||||
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
|
|
||||||
namespace App\DataProviders;
|
namespace App\DataProviders;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
class USStates
|
class USStates
|
||||||
{
|
{
|
||||||
protected static array $states = [
|
protected static array $states = [
|
||||||
@ -33866,11 +33868,146 @@ class USStates
|
|||||||
return self::$states;
|
return self::$states;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getState(string $zip): string
|
public static function getState(?string $zip = '90210'): string
|
||||||
{
|
{
|
||||||
if(isset(self::$zip_code_map[$zip]))
|
if(isset(self::$zip_code_map[$zip]))
|
||||||
return self::$zip_code_map[$zip];
|
return self::$zip_code_map[$zip];
|
||||||
|
|
||||||
|
$prefix_state = self::getStateFromThreeDigitPrefix($zip);
|
||||||
|
|
||||||
|
if($prefix_state)
|
||||||
|
return $prefix_state;
|
||||||
|
|
||||||
|
$zippo_response = self::getStateFromZippo($zip);
|
||||||
|
|
||||||
|
if($zippo_response)
|
||||||
|
return $zippo_response;
|
||||||
|
|
||||||
throw new \Exception('Zip code not found');
|
throw new \Exception('Zip code not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"post code": "90210",
|
||||||
|
"country": "United States",
|
||||||
|
"country abbreviation": "US",
|
||||||
|
"places": [
|
||||||
|
{
|
||||||
|
"place name": "Beverly Hills",
|
||||||
|
"longitude": "-118.4065",
|
||||||
|
"state": "California",
|
||||||
|
"state abbreviation": "CA",
|
||||||
|
"latitude": "34.0901"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
public static function getStateFromZippo($zip): mixed
|
||||||
|
{
|
||||||
|
|
||||||
|
$response = Http::get("https://api.zippopotam.us/us/{$zip}");
|
||||||
|
|
||||||
|
if($response->failed())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$data = $response->object();
|
||||||
|
|
||||||
|
if(isset($data->places[0])) {
|
||||||
|
return $data->places[0]->{'state abbreviation'};
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getStateFromThreeDigitPrefix($zip): mixed
|
||||||
|
{
|
||||||
|
|
||||||
|
/* 000 to 999 */
|
||||||
|
$zip_by_state = [
|
||||||
|
'--', '--', '--', '--', '--', 'NY', 'PR', 'PR', 'VI', 'PR', 'MA', 'MA', 'MA',
|
||||||
|
'MA', 'MA', 'MA', 'MA', 'MA', 'MA', 'MA', 'MA', 'MA', 'MA', 'MA', 'MA', 'MA',
|
||||||
|
'MA', 'MA', 'RI', 'RI', 'NH', 'NH', 'NH', 'NH', 'NH', 'NH', 'NH', 'NH', 'NH',
|
||||||
|
'ME', 'ME', 'ME', 'ME', 'ME', 'ME', 'ME', 'ME', 'ME', 'ME', 'ME', 'VT', 'VT',
|
||||||
|
'VT', 'VT', 'VT', 'MA', 'VT', 'VT', 'VT', 'VT', 'CT', 'CT', 'CT', 'CT', 'CT',
|
||||||
|
'CT', 'CT', 'CT', 'CT', 'CT', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ',
|
||||||
|
'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'AE',
|
||||||
|
'AE', 'AE', 'AE', 'AE', 'AE', 'AE', 'AE', 'AE', '--', 'NY', 'NY', 'NY', 'NY',
|
||||||
|
'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY',
|
||||||
|
'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY',
|
||||||
|
'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY',
|
||||||
|
'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA',
|
||||||
|
'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA',
|
||||||
|
'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA',
|
||||||
|
'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', '--', 'PA', 'PA',
|
||||||
|
'PA', 'PA', 'DE', 'DE', 'DE', 'DC', 'VA', 'DC', 'DC', 'DC', 'DC', 'MD', 'MD',
|
||||||
|
'MD', 'MD', 'MD', 'MD', 'MD', '--', 'MD', 'MD', 'MD', 'MD', 'MD', 'MD', 'VA',
|
||||||
|
'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA',
|
||||||
|
'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA',
|
||||||
|
'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV',
|
||||||
|
'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV', '--', 'NC', 'NC', 'NC',
|
||||||
|
'NC', 'NC', 'NC', 'NC', 'NC', 'NC', 'NC', 'NC', 'NC', 'NC', 'NC', 'NC', 'NC',
|
||||||
|
'NC', 'NC', 'NC', 'NC', 'SC', 'SC', 'SC', 'SC', 'SC', 'SC', 'SC', 'SC', 'SC',
|
||||||
|
'SC', 'GA', 'GA', 'GA', 'GA', 'GA', 'GA', 'GA', 'GA', 'GA', 'GA', 'GA', 'GA',
|
||||||
|
'GA', 'GA', 'GA', 'GA', 'GA', 'GA', 'GA', 'GA', 'FL', 'FL', 'FL', 'FL', 'FL',
|
||||||
|
'FL', 'FL', 'FL', 'FL', 'FL', 'FL', 'FL', 'FL', 'FL', 'FL', 'FL', 'FL', 'FL',
|
||||||
|
'FL', 'FL', 'AA', 'FL', 'FL', '--', 'FL', '--', 'FL', 'FL', '--', 'FL', 'AL',
|
||||||
|
'AL', 'AL', '--', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
||||||
|
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'TN', 'TN', 'TN', 'TN', 'TN', 'TN', 'TN',
|
||||||
|
'TN', 'TN', 'TN', 'TN', 'TN', 'TN', 'TN', 'TN', 'TN', 'MS', 'MS', 'MS', 'MS',
|
||||||
|
'MS', 'MS', 'MS', 'MS', 'MS', 'MS', 'MS', 'MS', 'GA', '--', 'KY', 'KY', 'KY',
|
||||||
|
'KY', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY',
|
||||||
|
'KY', 'KY', 'KY', '--', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY', '--',
|
||||||
|
'--', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH',
|
||||||
|
'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH',
|
||||||
|
'OH', 'OH', 'OH', 'OH', '--', 'IN', 'IN', 'IN', 'IN', 'IN', 'IN', 'IN', 'IN',
|
||||||
|
'IN', 'IN', 'IN', 'IN', 'IN', 'IN', 'IN', 'IN', 'IN', 'IN', 'IN', 'IN', 'MI',
|
||||||
|
'MI', 'MI', 'MI', 'MI', 'MI', 'MI', 'MI', 'MI', 'MI', 'MI', 'MI', 'MI', 'MI',
|
||||||
|
'MI', 'MI', 'MI', 'MI', 'MI', 'MI', 'IA', 'IA', 'IA', 'IA', 'IA', 'IA', 'IA',
|
||||||
|
'IA', 'IA', '--', 'IA', 'IA', 'IA', 'IA', 'IA', 'IA', 'IA', '--', '--', '--',
|
||||||
|
'IA', 'IA', 'IA', 'IA', 'IA', 'IA', 'IA', 'IA', 'IA', '--', 'WI', 'WI', 'WI',
|
||||||
|
'--', 'WI', 'WI', '--', 'WI', 'WI', 'WI', 'WI', 'WI', 'WI', 'WI', 'WI', 'WI',
|
||||||
|
'WI', 'WI', 'WI', 'WI', 'MN', 'MN', '--', 'MN', 'MN', 'MN', 'MN', 'MN', 'MN',
|
||||||
|
'MN', 'MN', 'MN', 'MN', 'MN', 'MN', 'MN', 'MN', 'MN', '--', 'DC', 'SD', 'SD',
|
||||||
|
'SD', 'SD', 'SD', 'SD', 'SD', 'SD', '--', '--', 'ND', 'ND', 'ND', 'ND', 'ND',
|
||||||
|
'ND', 'ND', 'ND', 'ND', '--', 'MT', 'MT', 'MT', 'MT', 'MT', 'MT', 'MT', 'MT',
|
||||||
|
'MT', 'MT', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL',
|
||||||
|
'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL', '--', 'IL', 'IL',
|
||||||
|
'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'MO', 'MO', '--', 'MO', 'MO', 'MO', 'MO',
|
||||||
|
'MO', 'MO', 'MO', 'MO', 'MO', '--', '--', 'MO', 'MO', 'MO', 'MO', 'MO', '--',
|
||||||
|
'MO', 'MO', 'MO', 'MO', 'MO', 'MO', 'MO', 'MO', 'MO', '--', 'KS', 'KS', 'KS',
|
||||||
|
'--', 'KS', 'KS', 'KS', 'KS', 'KS', 'KS', 'KS', 'KS', 'KS', 'KS', 'KS', 'KS',
|
||||||
|
'KS', 'KS', 'KS', 'KS', 'NE', 'NE', '--', 'NE', 'NE', 'NE', 'NE', 'NE', 'NE',
|
||||||
|
'NE', 'NE', 'NE', 'NE', 'NE', '--', '--', '--', '--', '--', '--', 'LA', 'LA',
|
||||||
|
'--', 'LA', 'LA', 'LA', 'LA', 'LA', 'LA', '--', 'LA', 'LA', 'LA', 'LA', 'LA',
|
||||||
|
'--', 'AR', 'AR', 'AR', 'AR', 'AR', 'AR', 'AR', 'AR', 'AR', 'AR', 'AR', 'AR',
|
||||||
|
'AR', 'AR', 'OK', 'OK', '--', 'TX', 'OK', 'OK', 'OK', 'OK', 'OK', 'OK', 'OK',
|
||||||
|
'OK', '--', 'OK', 'OK', 'OK', 'OK', 'OK', 'OK', 'OK', 'TX', 'TX', 'TX', 'TX',
|
||||||
|
'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX',
|
||||||
|
'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX',
|
||||||
|
'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX',
|
||||||
|
'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'CO', 'CO', 'CO', 'CO', 'CO', 'CO',
|
||||||
|
'CO', 'CO', 'CO', 'CO', 'CO', 'CO', 'CO', 'CO', 'CO', 'CO', 'CO', '--', '--',
|
||||||
|
'--', 'WY', 'WY', 'WY', 'WY', 'WY', 'WY', 'WY', 'WY', 'WY', 'WY', 'WY', 'WY',
|
||||||
|
'ID', 'ID', 'ID', 'ID', 'ID', 'ID', 'ID', '--', 'UT', 'UT', '--', 'UT', 'UT',
|
||||||
|
'UT', 'UT', 'UT', '--', '--', 'AZ', 'AZ', 'AZ', 'AZ', '--', 'AZ', 'AZ', 'AZ',
|
||||||
|
'--', 'AZ', 'AZ', '--', '--', 'AZ', 'AZ', 'AZ', '--', '--', '--', '--', 'NM',
|
||||||
|
'NM', '--', 'NM', 'NM', 'NM', '--', 'NM', 'NM', 'NM', 'NM', 'NM', 'NM', 'NM',
|
||||||
|
'NM', 'NM', '--', '--', '--', '--', 'NV', 'NV', '--', 'NV', 'NV', 'NV', '--',
|
||||||
|
'NV', 'NV', '--', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', '--',
|
||||||
|
'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA',
|
||||||
|
'CA', 'CA', 'CA', 'CA', 'CA', 'CA', '--', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA',
|
||||||
|
'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA',
|
||||||
|
'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA',
|
||||||
|
'AP', 'AP', 'AP', 'AP', 'AP', 'HI', 'HI', 'GU', 'OR', 'OR', 'OR', 'OR', 'OR',
|
||||||
|
'OR', 'OR', 'OR', 'OR', 'OR', 'WA', 'WA', 'WA', 'WA', 'WA', 'WA', 'WA', '--',
|
||||||
|
'WA', 'WA', 'WA', 'WA', 'WA', 'WA', 'WA', 'AK', 'AK', 'AK', 'AK', 'AK'
|
||||||
|
];
|
||||||
|
|
||||||
|
$prefix = substr($zip, 0, 3);
|
||||||
|
$index = intval($prefix);
|
||||||
|
/* converts prefix to integer */
|
||||||
|
return $zip_by_state[$index] == "--" ? false : $zip_by_state[$index];
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,10 @@
|
|||||||
namespace App\Export\CSV;
|
namespace App\Export\CSV;
|
||||||
|
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Models\Invoice;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
||||||
class BaseExport
|
class BaseExport
|
||||||
{
|
{
|
||||||
@ -46,6 +48,60 @@ class BaseExport
|
|||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function addInvoiceStatusFilter($query, $status): Builder
|
||||||
|
{
|
||||||
|
|
||||||
|
$status_parameters = explode(',', $status);
|
||||||
|
|
||||||
|
|
||||||
|
if(in_array('all', $status_parameters))
|
||||||
|
return $query;
|
||||||
|
|
||||||
|
$query->where(function ($nested) use ($status_parameters) {
|
||||||
|
|
||||||
|
$invoice_filters = [];
|
||||||
|
|
||||||
|
if (in_array('draft', $status_parameters)) {
|
||||||
|
$invoice_filters[] = Invoice::STATUS_DRAFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array('sent', $status_parameters)) {
|
||||||
|
$invoice_filters[] = Invoice::STATUS_SENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array('paid', $status_parameters)) {
|
||||||
|
$invoice_filters[] = Invoice::STATUS_PAID;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array('unpaid', $status_parameters)) {
|
||||||
|
$invoice_filters[] = Invoice::STATUS_SENT;
|
||||||
|
$invoice_filters[] = Invoice::STATUS_PARTIAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($invoice_filters) > 0) {
|
||||||
|
$nested->whereIn('status_id', $invoice_filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array('overdue', $status_parameters)) {
|
||||||
|
$nested->orWhereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||||
|
->where('due_date', '<', Carbon::now())
|
||||||
|
->orWhere('partial_due_date', '<', Carbon::now());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(in_array('viewed', $status_parameters)){
|
||||||
|
|
||||||
|
$nested->whereHas('invitations', function ($q){
|
||||||
|
$q->whereNotNull('viewed_date')->whereNotNull('deleted_at');
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
protected function addDateRange($query)
|
protected function addDateRange($query)
|
||||||
{
|
{
|
||||||
$date_range = $this->input['date_range'];
|
$date_range = $this->input['date_range'];
|
||||||
|
@ -11,13 +11,15 @@
|
|||||||
|
|
||||||
namespace App\Export\CSV;
|
namespace App\Export\CSV;
|
||||||
|
|
||||||
use App\Libraries\MultiDB;
|
use App\Utils\Ninja;
|
||||||
|
use App\Utils\Number;
|
||||||
|
use League\Csv\Writer;
|
||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Transformers\InvoiceTransformer;
|
use App\Libraries\MultiDB;
|
||||||
use App\Utils\Ninja;
|
use App\Export\CSV\BaseExport;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
use League\Csv\Writer;
|
use App\Transformers\InvoiceTransformer;
|
||||||
|
|
||||||
class InvoiceExport extends BaseExport
|
class InvoiceExport extends BaseExport
|
||||||
{
|
{
|
||||||
@ -63,6 +65,10 @@ class InvoiceExport extends BaseExport
|
|||||||
'terms' => 'terms',
|
'terms' => 'terms',
|
||||||
'total_taxes' => 'total_taxes',
|
'total_taxes' => 'total_taxes',
|
||||||
'currency_id' => 'currency_id',
|
'currency_id' => 'currency_id',
|
||||||
|
'payment_number' => 'payment_number',
|
||||||
|
'payment_date' => 'payment_date',
|
||||||
|
'payment_amount' => 'payment_amount',
|
||||||
|
'method' => 'method',
|
||||||
];
|
];
|
||||||
|
|
||||||
private array $decorate_keys = [
|
private array $decorate_keys = [
|
||||||
@ -107,6 +113,10 @@ class InvoiceExport extends BaseExport
|
|||||||
|
|
||||||
$query = $this->addDateRange($query);
|
$query = $this->addDateRange($query);
|
||||||
|
|
||||||
|
if(isset($this->input['status'])){
|
||||||
|
$query = $this->addInvoiceStatusFilter($query, $this->input['status']);
|
||||||
|
}
|
||||||
|
|
||||||
$query->cursor()
|
$query->cursor()
|
||||||
->each(function ($invoice) {
|
->each(function ($invoice) {
|
||||||
$this->csv->insertOne($this->buildRow($invoice));
|
$this->csv->insertOne($this->buildRow($invoice));
|
||||||
@ -152,6 +162,16 @@ class InvoiceExport extends BaseExport
|
|||||||
$entity['status'] = $invoice->stringStatus($invoice->status_id);
|
$entity['status'] = $invoice->stringStatus($invoice->status_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$payment_exists = $invoice->payments()->exists();
|
||||||
|
|
||||||
|
$entity['payment_number'] = $payment_exists ? $invoice->payments()->pluck('number')->implode(',') : '';
|
||||||
|
|
||||||
|
$entity['payment_date'] = $payment_exists ? $invoice->payments()->pluck('date')->implode(',') : '';
|
||||||
|
|
||||||
|
$entity['payment_amount'] = $payment_exists ? Number::formatMoney($invoice->payments()->sum('paymentables.amount'), $invoice->company) : ctrans('texts.unpaid');
|
||||||
|
|
||||||
|
$entity['method'] = $payment_exists ? $invoice->payments()->first()->translatedType() : "";
|
||||||
|
|
||||||
return $entity;
|
return $entity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ class PaymentExport extends BaseExport
|
|||||||
'transaction_reference' => 'transaction_reference',
|
'transaction_reference' => 'transaction_reference',
|
||||||
'type' => 'type_id',
|
'type' => 'type_id',
|
||||||
'vendor' => 'vendor_id',
|
'vendor' => 'vendor_id',
|
||||||
|
'invoices' => 'invoices',
|
||||||
];
|
];
|
||||||
|
|
||||||
private array $decorate_keys = [
|
private array $decorate_keys = [
|
||||||
@ -59,6 +60,7 @@ class PaymentExport extends BaseExport
|
|||||||
'currency',
|
'currency',
|
||||||
'exchange_currency',
|
'exchange_currency',
|
||||||
'type',
|
'type',
|
||||||
|
'invoices',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct(Company $company, array $input)
|
public function __construct(Company $company, array $input)
|
||||||
@ -154,6 +156,8 @@ class PaymentExport extends BaseExport
|
|||||||
$entity['gateway'] = $payment->gateway_type ? $payment->gateway_type->name : 'Unknown Type';
|
$entity['gateway'] = $payment->gateway_type ? $payment->gateway_type->name : 'Unknown Type';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$entity['invoices'] = $payment->invoices()->exists() ? $payment->invoices->pluck('number')->implode(',') : '';
|
||||||
|
|
||||||
return $entity;
|
return $entity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,7 +148,7 @@ class InvoiceItemSum
|
|||||||
|
|
||||||
$this->rule = new $class();
|
$this->rule = new $class();
|
||||||
$this->rule
|
$this->rule
|
||||||
->setInvoice($this->invoice)
|
->setEntity($this->invoice)
|
||||||
->init();
|
->init();
|
||||||
|
|
||||||
$this->calc_tax = true;
|
$this->calc_tax = true;
|
||||||
|
84
app/Http/Controllers/Reports/ARDetailReportController.php
Normal file
84
app/Http/Controllers/Reports/ARDetailReportController.php
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Reports;
|
||||||
|
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use App\Jobs\Report\SendToAdmin;
|
||||||
|
use App\Services\Report\ARDetailReport;
|
||||||
|
use App\Http\Controllers\BaseController;
|
||||||
|
use App\Http\Requests\Report\GenericReportRequest;
|
||||||
|
|
||||||
|
class ARDetailReportController extends BaseController
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
|
||||||
|
private string $filename = 'ar_detail.csv';
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @OA\Post(
|
||||||
|
* path="/api/v1/reports/tasks",
|
||||||
|
* operationId="getTaskReport",
|
||||||
|
* tags={"reports"},
|
||||||
|
* summary="Task reports",
|
||||||
|
* description="Export task reports",
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||||
|
* @OA\RequestBody(
|
||||||
|
* required=true,
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/GenericReportSchema")
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response=200,
|
||||||
|
* description="success",
|
||||||
|
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||||
|
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||||
|
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response=422,
|
||||||
|
* description="Validation error",
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response="default",
|
||||||
|
* description="Unexpected Error",
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||||
|
* ),
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
public function __invoke(GenericReportRequest $request)
|
||||||
|
{
|
||||||
|
if ($request->has('send_email') && $request->get('send_email')) {
|
||||||
|
SendToAdmin::dispatch(auth()->user()->company(), $request->all(), ARDetailReport::class, $this->filename);
|
||||||
|
|
||||||
|
return response()->json(['message' => 'working...'], 200);
|
||||||
|
}
|
||||||
|
// expect a list of visible fields, or use the default
|
||||||
|
|
||||||
|
$export = new ARDetailReport(auth()->user()->company(), $request->all());
|
||||||
|
|
||||||
|
$csv = $export->run();
|
||||||
|
|
||||||
|
$headers = [
|
||||||
|
'Content-Disposition' => 'attachment',
|
||||||
|
'Content-Type' => 'text/csv',
|
||||||
|
];
|
||||||
|
|
||||||
|
return response()->streamDownload(function () use ($csv) {
|
||||||
|
echo $csv;
|
||||||
|
}, $this->filename, $headers);
|
||||||
|
}
|
||||||
|
}
|
84
app/Http/Controllers/Reports/ARSummaryReportController.php
Normal file
84
app/Http/Controllers/Reports/ARSummaryReportController.php
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Reports;
|
||||||
|
|
||||||
|
use App\Http\Controllers\BaseController;
|
||||||
|
use App\Http\Requests\Report\GenericReportRequest;
|
||||||
|
use App\Jobs\Report\SendToAdmin;
|
||||||
|
use App\Services\Report\ARSummaryReport;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
|
||||||
|
class ARSummaryReportController extends BaseController
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
|
||||||
|
private string $filename = 'ar_summary.csv';
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @OA\Post(
|
||||||
|
* path="/api/v1/reports/tasks",
|
||||||
|
* operationId="getTaskReport",
|
||||||
|
* tags={"reports"},
|
||||||
|
* summary="Task reports",
|
||||||
|
* description="Export task reports",
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||||
|
* @OA\RequestBody(
|
||||||
|
* required=true,
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/GenericReportSchema")
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response=200,
|
||||||
|
* description="success",
|
||||||
|
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||||
|
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||||
|
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response=422,
|
||||||
|
* description="Validation error",
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response="default",
|
||||||
|
* description="Unexpected Error",
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||||
|
* ),
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
public function __invoke(GenericReportRequest $request)
|
||||||
|
{
|
||||||
|
if ($request->has('send_email') && $request->get('send_email')) {
|
||||||
|
SendToAdmin::dispatch(auth()->user()->company(), $request->all(), ARSummaryReport::class, $this->filename);
|
||||||
|
|
||||||
|
return response()->json(['message' => 'working...'], 200);
|
||||||
|
}
|
||||||
|
// expect a list of visible fields, or use the default
|
||||||
|
|
||||||
|
$export = new ARSummaryReport(auth()->user()->company(), $request->all());
|
||||||
|
|
||||||
|
$csv = $export->run();
|
||||||
|
|
||||||
|
$headers = [
|
||||||
|
'Content-Disposition' => 'attachment',
|
||||||
|
'Content-Type' => 'text/csv',
|
||||||
|
];
|
||||||
|
|
||||||
|
return response()->streamDownload(function () use ($csv) {
|
||||||
|
echo $csv;
|
||||||
|
}, $this->filename, $headers);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Reports;
|
||||||
|
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use App\Jobs\Report\SendToAdmin;
|
||||||
|
use App\Http\Controllers\BaseController;
|
||||||
|
use App\Services\Report\ARSummaryReport;
|
||||||
|
use App\Services\Report\ClientBalanceReport;
|
||||||
|
use App\Http\Requests\Report\GenericReportRequest;
|
||||||
|
|
||||||
|
class ClientBalanceReportController extends BaseController
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
|
||||||
|
private string $filename = 'client_balance.csv';
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @OA\Post(
|
||||||
|
* path="/api/v1/reports/tasks",
|
||||||
|
* operationId="getTaskReport",
|
||||||
|
* tags={"reports"},
|
||||||
|
* summary="Task reports",
|
||||||
|
* description="Export task reports",
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||||
|
* @OA\RequestBody(
|
||||||
|
* required=true,
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/GenericReportSchema")
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response=200,
|
||||||
|
* description="success",
|
||||||
|
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||||
|
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||||
|
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response=422,
|
||||||
|
* description="Validation error",
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response="default",
|
||||||
|
* description="Unexpected Error",
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||||
|
* ),
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
public function __invoke(GenericReportRequest $request)
|
||||||
|
{
|
||||||
|
if ($request->has('send_email') && $request->get('send_email')) {
|
||||||
|
SendToAdmin::dispatch(auth()->user()->company(), $request->all(), ClientBalanceReport::class, $this->filename);
|
||||||
|
|
||||||
|
return response()->json(['message' => 'working...'], 200);
|
||||||
|
}
|
||||||
|
// expect a list of visible fields, or use the default
|
||||||
|
|
||||||
|
$export = new ClientBalanceReport(auth()->user()->company(), $request->all());
|
||||||
|
|
||||||
|
$csv = $export->run();
|
||||||
|
|
||||||
|
$headers = [
|
||||||
|
'Content-Disposition' => 'attachment',
|
||||||
|
'Content-Type' => 'text/csv',
|
||||||
|
];
|
||||||
|
|
||||||
|
return response()->streamDownload(function () use ($csv) {
|
||||||
|
echo $csv;
|
||||||
|
}, $this->filename, $headers);
|
||||||
|
}
|
||||||
|
}
|
85
app/Http/Controllers/Reports/ClientSalesReportController.php
Normal file
85
app/Http/Controllers/Reports/ClientSalesReportController.php
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Reports;
|
||||||
|
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use App\Jobs\Report\SendToAdmin;
|
||||||
|
use App\Http\Controllers\BaseController;
|
||||||
|
use App\Services\Report\ClientSalesReport;
|
||||||
|
use App\Services\Report\ClientBalanceReport;
|
||||||
|
use App\Http\Requests\Report\GenericReportRequest;
|
||||||
|
|
||||||
|
class ClientSalesReportController extends BaseController
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
|
||||||
|
private string $filename = 'client_sales.csv';
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @OA\Post(
|
||||||
|
* path="/api/v1/reports/tasks",
|
||||||
|
* operationId="getTaskReport",
|
||||||
|
* tags={"reports"},
|
||||||
|
* summary="Task reports",
|
||||||
|
* description="Export task reports",
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||||
|
* @OA\RequestBody(
|
||||||
|
* required=true,
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/GenericReportSchema")
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response=200,
|
||||||
|
* description="success",
|
||||||
|
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||||
|
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||||
|
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response=422,
|
||||||
|
* description="Validation error",
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response="default",
|
||||||
|
* description="Unexpected Error",
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||||
|
* ),
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
public function __invoke(GenericReportRequest $request)
|
||||||
|
{
|
||||||
|
if ($request->has('send_email') && $request->get('send_email')) {
|
||||||
|
SendToAdmin::dispatch(auth()->user()->company(), $request->all(), ClientSalesReport::class, $this->filename);
|
||||||
|
|
||||||
|
return response()->json(['message' => 'working...'], 200);
|
||||||
|
}
|
||||||
|
// expect a list of visible fields, or use the default
|
||||||
|
|
||||||
|
$export = new ClientSalesReport(auth()->user()->company(), $request->all());
|
||||||
|
|
||||||
|
$csv = $export->run();
|
||||||
|
|
||||||
|
$headers = [
|
||||||
|
'Content-Disposition' => 'attachment',
|
||||||
|
'Content-Type' => 'text/csv',
|
||||||
|
];
|
||||||
|
|
||||||
|
return response()->streamDownload(function () use ($csv) {
|
||||||
|
echo $csv;
|
||||||
|
}, $this->filename, $headers);
|
||||||
|
}
|
||||||
|
}
|
85
app/Http/Controllers/Reports/TaxSummaryReportController.php
Normal file
85
app/Http/Controllers/Reports/TaxSummaryReportController.php
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Reports;
|
||||||
|
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use App\Jobs\Report\SendToAdmin;
|
||||||
|
use App\Http\Controllers\BaseController;
|
||||||
|
use App\Services\Report\TaxSummaryReport;
|
||||||
|
use App\Services\Report\ClientSalesReport;
|
||||||
|
use App\Http\Requests\Report\GenericReportRequest;
|
||||||
|
|
||||||
|
class TaxSummaryReportController extends BaseController
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
|
||||||
|
private string $filename = 'tax_summary.csv';
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @OA\Post(
|
||||||
|
* path="/api/v1/reports/tasks",
|
||||||
|
* operationId="getTaskReport",
|
||||||
|
* tags={"reports"},
|
||||||
|
* summary="Task reports",
|
||||||
|
* description="Export task reports",
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||||
|
* @OA\RequestBody(
|
||||||
|
* required=true,
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/GenericReportSchema")
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response=200,
|
||||||
|
* description="success",
|
||||||
|
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||||
|
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||||
|
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response=422,
|
||||||
|
* description="Validation error",
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response="default",
|
||||||
|
* description="Unexpected Error",
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||||
|
* ),
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
public function __invoke(GenericReportRequest $request)
|
||||||
|
{
|
||||||
|
if ($request->has('send_email') && $request->get('send_email')) {
|
||||||
|
SendToAdmin::dispatch(auth()->user()->company(), $request->all(), TaxSummaryReport::class, $this->filename);
|
||||||
|
|
||||||
|
return response()->json(['message' => 'working...'], 200);
|
||||||
|
}
|
||||||
|
// expect a list of visible fields, or use the default
|
||||||
|
|
||||||
|
$export = new TaxSummaryReport(auth()->user()->company(), $request->all());
|
||||||
|
|
||||||
|
$csv = $export->run();
|
||||||
|
|
||||||
|
$headers = [
|
||||||
|
'Content-Disposition' => 'attachment',
|
||||||
|
'Content-Type' => 'text/csv',
|
||||||
|
];
|
||||||
|
|
||||||
|
return response()->streamDownload(function () use ($csv) {
|
||||||
|
echo $csv;
|
||||||
|
}, $this->filename, $headers);
|
||||||
|
}
|
||||||
|
}
|
85
app/Http/Controllers/Reports/UserSalesReportController.php
Normal file
85
app/Http/Controllers/Reports/UserSalesReportController.php
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Reports;
|
||||||
|
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use App\Jobs\Report\SendToAdmin;
|
||||||
|
use App\Http\Controllers\BaseController;
|
||||||
|
use App\Services\Report\UserSalesReport;
|
||||||
|
use App\Services\Report\TaxSummaryReport;
|
||||||
|
use App\Http\Requests\Report\GenericReportRequest;
|
||||||
|
|
||||||
|
class UserSalesReportController extends BaseController
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
|
||||||
|
private string $filename = 'user_sales.csv';
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @OA\Post(
|
||||||
|
* path="/api/v1/reports/tasks",
|
||||||
|
* operationId="getTaskReport",
|
||||||
|
* tags={"reports"},
|
||||||
|
* summary="Task reports",
|
||||||
|
* description="Export task reports",
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||||
|
* @OA\RequestBody(
|
||||||
|
* required=true,
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/GenericReportSchema")
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response=200,
|
||||||
|
* description="success",
|
||||||
|
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||||
|
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||||
|
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response=422,
|
||||||
|
* description="Validation error",
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response="default",
|
||||||
|
* description="Unexpected Error",
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||||
|
* ),
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
public function __invoke(GenericReportRequest $request)
|
||||||
|
{
|
||||||
|
if ($request->has('send_email') && $request->get('send_email')) {
|
||||||
|
SendToAdmin::dispatch(auth()->user()->company(), $request->all(), UserSalesReport::class, $this->filename);
|
||||||
|
|
||||||
|
return response()->json(['message' => 'working...'], 200);
|
||||||
|
}
|
||||||
|
// expect a list of visible fields, or use the default
|
||||||
|
|
||||||
|
$export = new UserSalesReport(auth()->user()->company(), $request->all());
|
||||||
|
|
||||||
|
$csv = $export->run();
|
||||||
|
|
||||||
|
$headers = [
|
||||||
|
'Content-Disposition' => 'attachment',
|
||||||
|
'Content-Type' => 'text/csv',
|
||||||
|
];
|
||||||
|
|
||||||
|
return response()->streamDownload(function () use ($csv) {
|
||||||
|
echo $csv;
|
||||||
|
}, $this->filename, $headers);
|
||||||
|
}
|
||||||
|
}
|
@ -33,6 +33,7 @@ class GenericReportRequest extends Request
|
|||||||
'start_date' => 'bail|required_if:date_range,custom|nullable|date',
|
'start_date' => 'bail|required_if:date_range,custom|nullable|date',
|
||||||
'report_keys' => 'present|array',
|
'report_keys' => 'present|array',
|
||||||
'send_email' => 'required|bool',
|
'send_email' => 'required|bool',
|
||||||
|
'status' => 'sometimes|string|nullable|in:all,draft,sent,viewed,paid,unpaid,overdue',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,8 +56,39 @@ class PaymentNotification implements ShouldQueue
|
|||||||
$this->trackRevenue($event);
|
$this->trackRevenue($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($payment->is_manual)
|
/* Manual Payment Notifications */
|
||||||
|
if($payment->is_manual){
|
||||||
|
|
||||||
|
foreach ($payment->company->company_users as $company_user) {
|
||||||
|
$user = $company_user->user;
|
||||||
|
|
||||||
|
$methods = $this->findUserEntityNotificationType(
|
||||||
|
$payment,
|
||||||
|
$company_user,
|
||||||
|
[
|
||||||
|
'payment_manual',
|
||||||
|
'payment_manual_all',
|
||||||
|
'payment_manual_user',
|
||||||
|
'all_notifications', ]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (($key = array_search('mail', $methods)) !== false) {
|
||||||
|
unset($methods[$key]);
|
||||||
|
|
||||||
|
$nmo = new NinjaMailerObject;
|
||||||
|
$nmo->mailable = new NinjaMailer((new EntityPaidObject($payment))->build());
|
||||||
|
$nmo->company = $event->company;
|
||||||
|
$nmo->settings = $event->company->settings;
|
||||||
|
$nmo->to_user = $user;
|
||||||
|
|
||||||
|
(new NinjaMailerJob($nmo))->handle();
|
||||||
|
|
||||||
|
$nmo = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*User notifications*/
|
/*User notifications*/
|
||||||
foreach ($payment->company->company_users as $company_user) {
|
foreach ($payment->company->company_users as $company_user) {
|
||||||
|
@ -40,6 +40,7 @@ class ARDetailReport extends BaseExport
|
|||||||
|
|
||||||
public array $report_keys = [
|
public array $report_keys = [
|
||||||
'date',
|
'date',
|
||||||
|
'due_date',
|
||||||
'invoice_number',
|
'invoice_number',
|
||||||
'status',
|
'status',
|
||||||
'client_name',
|
'client_name',
|
||||||
@ -114,6 +115,7 @@ class ARDetailReport extends BaseExport
|
|||||||
$client = $invoice->client;
|
$client = $invoice->client;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
$this->translateDate($invoice->date, $this->company->date_format(), $this->company->locale()),
|
||||||
$this->translateDate($invoice->due_date, $this->company->date_format(), $this->company->locale()),
|
$this->translateDate($invoice->due_date, $this->company->date_format(), $this->company->locale()),
|
||||||
$invoice->number,
|
$invoice->number,
|
||||||
$invoice->stringStatus($invoice->status_id),
|
$invoice->stringStatus($invoice->status_id),
|
||||||
|
@ -36,6 +36,7 @@ class ClientBalanceReport extends BaseExport
|
|||||||
'client_name',
|
'client_name',
|
||||||
'client_number',
|
'client_number',
|
||||||
'id_number',
|
'id_number',
|
||||||
|
'invoices',
|
||||||
'invoice_balance',
|
'invoice_balance',
|
||||||
'credit_balance',
|
'credit_balance',
|
||||||
];
|
];
|
||||||
@ -68,7 +69,7 @@ class ClientBalanceReport extends BaseExport
|
|||||||
$this->csv->insertOne([]);
|
$this->csv->insertOne([]);
|
||||||
$this->csv->insertOne([]);
|
$this->csv->insertOne([]);
|
||||||
$this->csv->insertOne([]);
|
$this->csv->insertOne([]);
|
||||||
$this->csv->insertOne([ctrans('texts.customer_balance_report')]);
|
$this->csv->insertOne([ctrans('texts.client_balance_report')]);
|
||||||
$this->csv->insertOne([ctrans('texts.created_on'),' ',$this->translateDate(now()->format('Y-m-d'), $this->company->date_format(), $this->company->locale())]);
|
$this->csv->insertOne([ctrans('texts.created_on'),' ',$this->translateDate(now()->format('Y-m-d'), $this->company->date_format(), $this->company->locale())]);
|
||||||
|
|
||||||
if (count($this->input['report_keys']) == 0) {
|
if (count($this->input['report_keys']) == 0) {
|
||||||
|
@ -149,7 +149,7 @@ class ClientTransformer extends EntityTransformer
|
|||||||
'number' => (string) $client->number ?: '',
|
'number' => (string) $client->number ?: '',
|
||||||
'has_valid_vat_number' => (bool) $client->has_valid_vat_number,
|
'has_valid_vat_number' => (bool) $client->has_valid_vat_number,
|
||||||
'is_tax_exempt' => (bool) $client->is_tax_exempt,
|
'is_tax_exempt' => (bool) $client->is_tax_exempt,
|
||||||
'tax_data' => $client->tax_data ?: '',
|
// 'tax_data' => $client->tax_data ?: '',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ class InvoiceTransformer extends EntityTransformer
|
|||||||
'paid_to_date' => (float) $invoice->paid_to_date,
|
'paid_to_date' => (float) $invoice->paid_to_date,
|
||||||
'subscription_id' => $this->encodePrimaryKey($invoice->subscription_id),
|
'subscription_id' => $this->encodePrimaryKey($invoice->subscription_id),
|
||||||
'auto_bill_enabled' => (bool) $invoice->auto_bill_enabled,
|
'auto_bill_enabled' => (bool) $invoice->auto_bill_enabled,
|
||||||
'tax_data' => $invoice->tax_data ?: '',
|
// 'tax_data' => $invoice->tax_data ?: '',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5062,6 +5062,14 @@ $LANG = array(
|
|||||||
'here' => 'here',
|
'here' => 'here',
|
||||||
'industry_Restaurant & Catering' => 'Restaurant & Catering',
|
'industry_Restaurant & Catering' => 'Restaurant & Catering',
|
||||||
'show_credits_table' => 'Show Credits Table',
|
'show_credits_table' => 'Show Credits Table',
|
||||||
|
'manual_payment' => 'Payment Manual',
|
||||||
|
'tax_summary_report' => 'Tax Summary Report',
|
||||||
|
'tax_category' => 'Tax Category',
|
||||||
|
'physical_goods' => 'Physical Goods',
|
||||||
|
'digital_products' => 'Digital Products',
|
||||||
|
'services' => 'Services',
|
||||||
|
'shipping' => 'Shipping',
|
||||||
|
'tax_exempt' => 'Tax Exempt',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -1333,7 +1333,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
|
|||||||
'finish_setup' => 'Terminer la configuration',
|
'finish_setup' => 'Terminer la configuration',
|
||||||
'created_wepay_confirmation_required' => 'Veuillez vérifier vos courriel et confirmer votre adresse courriel avec WePay.',
|
'created_wepay_confirmation_required' => 'Veuillez vérifier vos courriel et confirmer votre adresse courriel avec WePay.',
|
||||||
'switch_to_wepay' => 'Changer pour WePay',
|
'switch_to_wepay' => 'Changer pour WePay',
|
||||||
'switch' => 'Changer',
|
'switch' => 'Commutateur',
|
||||||
'restore_account_gateway' => 'Restaurer la passerelle de paiement',
|
'restore_account_gateway' => 'Restaurer la passerelle de paiement',
|
||||||
'restored_account_gateway' => 'La passerelle de paiement a été restaurée',
|
'restored_account_gateway' => 'La passerelle de paiement a été restaurée',
|
||||||
'united_states' => 'États-Unis',
|
'united_states' => 'États-Unis',
|
||||||
@ -3333,7 +3333,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
|
|||||||
'accent_color' => 'Couleur de mise en évidence',
|
'accent_color' => 'Couleur de mise en évidence',
|
||||||
'comma_sparated_list' => 'Liste séparée par virgule',
|
'comma_sparated_list' => 'Liste séparée par virgule',
|
||||||
'single_line_text' => 'Ligne de texte simple',
|
'single_line_text' => 'Ligne de texte simple',
|
||||||
'multi_line_text' => 'Multiligne de texte',
|
'multi_line_text' => 'Zone de texte multiligne',
|
||||||
'dropdown' => 'Liste déroulante',
|
'dropdown' => 'Liste déroulante',
|
||||||
'field_type' => 'Type de champ',
|
'field_type' => 'Type de champ',
|
||||||
'recover_password_email_sent' => 'Un courriel a été envoyé pour la récupération du mot de passe',
|
'recover_password_email_sent' => 'Un courriel a été envoyé pour la récupération du mot de passe',
|
||||||
@ -4609,7 +4609,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
|
|||||||
'add' => 'Ajouter',
|
'add' => 'Ajouter',
|
||||||
'last_sent_template' => 'Modèle pour dernier envoi',
|
'last_sent_template' => 'Modèle pour dernier envoi',
|
||||||
'enable_flexible_search' => 'Activer la recherche flexible',
|
'enable_flexible_search' => 'Activer la recherche flexible',
|
||||||
'enable_flexible_search_help' => 'Match non-contiguous characters, ie. "ct" matches "cat"',
|
'enable_flexible_search_help' => 'Correspondance de caractères non contigus, ex. "ct" pour "cat"',
|
||||||
'vendor_details' => 'Informations du fournisseur',
|
'vendor_details' => 'Informations du fournisseur',
|
||||||
'purchase_order_details' => 'Détails du bon de commande',
|
'purchase_order_details' => 'Détails du bon de commande',
|
||||||
'qr_iban' => 'QR IBAN',
|
'qr_iban' => 'QR IBAN',
|
||||||
@ -4805,7 +4805,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
|
|||||||
'task_update_authorization_error' => 'Permission d\'accès insuffisante ou tâche verrouillée',
|
'task_update_authorization_error' => 'Permission d\'accès insuffisante ou tâche verrouillée',
|
||||||
'cash_vs_accrual' => 'Comptabilité d\'exercice',
|
'cash_vs_accrual' => 'Comptabilité d\'exercice',
|
||||||
'cash_vs_accrual_help' => 'Activer pour comptabilité d\'exercice. Désactiver pour comptabilité d\'encaisse',
|
'cash_vs_accrual_help' => 'Activer pour comptabilité d\'exercice. Désactiver pour comptabilité d\'encaisse',
|
||||||
'expense_paid_report' => 'Expensed reporting',
|
'expense_paid_report' => 'Rapport des déboursés',
|
||||||
'expense_paid_report_help' => 'Activer pour un rapport de toutes les dépenses. Désactiver pour un rapport des dépenses payées seulement',
|
'expense_paid_report_help' => 'Activer pour un rapport de toutes les dépenses. Désactiver pour un rapport des dépenses payées seulement',
|
||||||
'online_payment_email_help' => 'Envoyer un courriel lorsque un paiement en ligne à été fait',
|
'online_payment_email_help' => 'Envoyer un courriel lorsque un paiement en ligne à été fait',
|
||||||
'manual_payment_email_help' => 'Envoyer un courriel lorsque un paiement a été saisi manuellement',
|
'manual_payment_email_help' => 'Envoyer un courriel lorsque un paiement a été saisi manuellement',
|
||||||
|
@ -90,12 +90,18 @@ use App\Http\Controllers\Reports\PaymentReportController;
|
|||||||
use App\Http\Controllers\Reports\ProductReportController;
|
use App\Http\Controllers\Reports\ProductReportController;
|
||||||
use App\Http\Controllers\Reports\ProfitAndLossController;
|
use App\Http\Controllers\Reports\ProfitAndLossController;
|
||||||
use App\Http\Controllers\Reports\ActivityReportController;
|
use App\Http\Controllers\Reports\ActivityReportController;
|
||||||
|
use App\Http\Controllers\Reports\ARDetailReportController;
|
||||||
use App\Http\Controllers\Reports\DocumentReportController;
|
use App\Http\Controllers\Reports\DocumentReportController;
|
||||||
|
use App\Http\Controllers\Reports\ARSummaryReportController;
|
||||||
use App\Http\Controllers\Reports\QuoteItemReportController;
|
use App\Http\Controllers\Reports\QuoteItemReportController;
|
||||||
|
use App\Http\Controllers\Reports\UserSalesReportController;
|
||||||
|
use App\Http\Controllers\Reports\TaxSummaryReportController;
|
||||||
use App\Http\Controllers\Support\Messages\SendingController;
|
use App\Http\Controllers\Support\Messages\SendingController;
|
||||||
|
use App\Http\Controllers\Reports\ClientSalesReportController;
|
||||||
use App\Http\Controllers\Reports\InvoiceItemReportController;
|
use App\Http\Controllers\Reports\InvoiceItemReportController;
|
||||||
use App\Http\Controllers\PaymentNotificationWebhookController;
|
use App\Http\Controllers\PaymentNotificationWebhookController;
|
||||||
use App\Http\Controllers\Reports\ProductSalesReportController;
|
use App\Http\Controllers\Reports\ProductSalesReportController;
|
||||||
|
use App\Http\Controllers\Reports\ClientBalanceReportController;
|
||||||
use App\Http\Controllers\Reports\ClientContactReportController;
|
use App\Http\Controllers\Reports\ClientContactReportController;
|
||||||
use App\Http\Controllers\Reports\RecurringInvoiceReportController;
|
use App\Http\Controllers\Reports\RecurringInvoiceReportController;
|
||||||
|
|
||||||
@ -286,6 +292,13 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale']
|
|||||||
Route::post('reports/tasks', TaskReportController::class);
|
Route::post('reports/tasks', TaskReportController::class);
|
||||||
Route::post('reports/profitloss', ProfitAndLossController::class);
|
Route::post('reports/profitloss', ProfitAndLossController::class);
|
||||||
|
|
||||||
|
Route::post('reports/ar_detail_report', ARDetailReportController::class);
|
||||||
|
Route::post('reports/ar_summary_report', ARSummaryReportController::class);
|
||||||
|
Route::post('reports/client_balance_report', ClientBalanceReportController::class);
|
||||||
|
Route::post('reports/client_sales_report', ClientSalesReportController::class);
|
||||||
|
Route::post('reports/tax_summary_report', TaxSummaryReportController::class);
|
||||||
|
Route::post('reports/user_sales_report', UserSalesReportController::class);
|
||||||
|
|
||||||
Route::resource('task_schedulers', TaskSchedulerController::class);
|
Route::resource('task_schedulers', TaskSchedulerController::class);
|
||||||
Route::post('task_schedulers/bulk', [TaskSchedulerController::class, 'bulk'])->name('task_schedulers.bulk');
|
Route::post('task_schedulers/bulk', [TaskSchedulerController::class, 'bulk'])->name('task_schedulers.bulk');
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ use App\Services\Report\ARDetailReport;
|
|||||||
use App\Services\Report\UserSalesReport;
|
use App\Services\Report\UserSalesReport;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||||
|
use Tests\MockAccountData;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,6 +45,7 @@ class ARDetailReportTest extends TestCase
|
|||||||
);
|
);
|
||||||
|
|
||||||
$this->withoutExceptionHandling();
|
$this->withoutExceptionHandling();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public $company;
|
public $company;
|
||||||
|
145
tests/Feature/Export/ReportApiTest.php
Normal file
145
tests/Feature/Export/ReportApiTest.php
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tests\Feature\Export;
|
||||||
|
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||||
|
use Tests\MockAccountData;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
class ReportApiTest extends TestCase
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
use MockAccountData;
|
||||||
|
|
||||||
|
public $faker;
|
||||||
|
|
||||||
|
protected function setUp() :void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->faker = \Faker\Factory::create();
|
||||||
|
|
||||||
|
$this->withoutMiddleware(
|
||||||
|
ThrottleRequests::class
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->withoutExceptionHandling();
|
||||||
|
|
||||||
|
$this->makeTestData();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUserSalesReportApiRoute()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'send_email' => false,
|
||||||
|
'date_range' => 'all',
|
||||||
|
'report_keys' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->postJson('/api/v1/reports/user_sales_report', $data)
|
||||||
|
->assertStatus(200);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testTaxSummaryReportApiRoute()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'send_email' => false,
|
||||||
|
'date_range' => 'all',
|
||||||
|
'report_keys' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->postJson('/api/v1/reports/tax_summary_report', $data)
|
||||||
|
->assertStatus(200);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testClientSalesReportApiRoute()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'send_email' => false,
|
||||||
|
'date_range' => 'all',
|
||||||
|
'report_keys' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->postJson('/api/v1/reports/client_sales_report', $data)
|
||||||
|
->assertStatus(200);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testArDetailReportApiRoute()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'send_email' => false,
|
||||||
|
'date_range' => 'all',
|
||||||
|
'report_keys' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->postJson('/api/v1/reports/ar_detail_report', $data)
|
||||||
|
->assertStatus(200);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testArSummaryReportApiRoute()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'send_email' => false,
|
||||||
|
'date_range' => 'all',
|
||||||
|
'report_keys' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->postJson('/api/v1/reports/ar_summary_report', $data)
|
||||||
|
->assertStatus(200);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClientBalanceReportApiRoute()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'send_email' => false,
|
||||||
|
'date_range' => 'all',
|
||||||
|
'report_keys' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->postJson('/api/v1/reports/client_balance_report', $data)
|
||||||
|
->assertStatus(200);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -480,14 +480,14 @@ class SchedulerTest extends TestCase
|
|||||||
$c = Client::factory()->create([
|
$c = Client::factory()->create([
|
||||||
'company_id' => $this->company->id,
|
'company_id' => $this->company->id,
|
||||||
'user_id' => $this->user->id,
|
'user_id' => $this->user->id,
|
||||||
'number' => rand(1000, 100000),
|
'number' => rand(1000, 10000000),
|
||||||
'name' => 'A fancy client'
|
'name' => 'A fancy client'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$c2 = Client::factory()->create([
|
$c2 = Client::factory()->create([
|
||||||
'company_id' => $this->company->id,
|
'company_id' => $this->company->id,
|
||||||
'user_id' => $this->user->id,
|
'user_id' => $this->user->id,
|
||||||
'number' => rand(1000, 100000),
|
'number' => rand(1000, 10000000),
|
||||||
'name' => 'A fancy client'
|
'name' => 'A fancy client'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -349,7 +349,7 @@ class EuTaxTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$process = new Rule();
|
$process = new Rule();
|
||||||
$process->setInvoice($invoice);
|
$process->setEntity($invoice);
|
||||||
$process->init();
|
$process->init();
|
||||||
|
|
||||||
$this->assertEquals('EU', $process->seller_region);
|
$this->assertEquals('EU', $process->seller_region);
|
||||||
@ -403,7 +403,7 @@ class EuTaxTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$process = new Rule();
|
$process = new Rule();
|
||||||
$process->setInvoice($invoice);
|
$process->setEntity($invoice);
|
||||||
$process->init();
|
$process->init();
|
||||||
|
|
||||||
$this->assertEquals('EU', $process->seller_region);
|
$this->assertEquals('EU', $process->seller_region);
|
||||||
@ -458,7 +458,7 @@ class EuTaxTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$process = new Rule();
|
$process = new Rule();
|
||||||
$process->setInvoice($invoice);
|
$process->setEntity($invoice);
|
||||||
$process->init();
|
$process->init();
|
||||||
|
|
||||||
$this->assertEquals('EU', $process->seller_region);
|
$this->assertEquals('EU', $process->seller_region);
|
||||||
@ -513,7 +513,7 @@ class EuTaxTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$process = new Rule();
|
$process = new Rule();
|
||||||
$process->setInvoice($invoice);
|
$process->setEntity($invoice);
|
||||||
$process->init();
|
$process->init();
|
||||||
|
|
||||||
$this->assertInstanceOf(Rule::class, $process);
|
$this->assertInstanceOf(Rule::class, $process);
|
||||||
@ -564,7 +564,7 @@ class EuTaxTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$process = new Rule();
|
$process = new Rule();
|
||||||
$process->setInvoice($invoice);
|
$process->setEntity($invoice);
|
||||||
$process->init();
|
$process->init();
|
||||||
|
|
||||||
|
|
||||||
@ -615,7 +615,7 @@ class EuTaxTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$process = new Rule();
|
$process = new Rule();
|
||||||
$process->setInvoice($invoice);
|
$process->setEntity($invoice);
|
||||||
$process->init();
|
$process->init();
|
||||||
|
|
||||||
|
|
||||||
@ -666,7 +666,7 @@ class EuTaxTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$process = new Rule();
|
$process = new Rule();
|
||||||
$process->setInvoice($invoice);
|
$process->setEntity($invoice);
|
||||||
$process->init();
|
$process->init();
|
||||||
|
|
||||||
|
|
||||||
@ -717,7 +717,7 @@ class EuTaxTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$process = new Rule();
|
$process = new Rule();
|
||||||
$process->setInvoice($invoice);
|
$process->setEntity($invoice);
|
||||||
$process->init();
|
$process->init();
|
||||||
|
|
||||||
$this->assertInstanceOf(Rule::class, $process);
|
$this->assertInstanceOf(Rule::class, $process);
|
||||||
@ -766,7 +766,7 @@ class EuTaxTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$process = new Rule();
|
$process = new Rule();
|
||||||
$process->setInvoice($invoice);
|
$process->setEntity($invoice);
|
||||||
$process->init();
|
$process->init();
|
||||||
|
|
||||||
|
|
||||||
|
@ -102,10 +102,13 @@ class SumTaxTest extends TestCase
|
|||||||
$this->company->tax_data = $tax_data;
|
$this->company->tax_data = $tax_data;
|
||||||
$this->company->save();
|
$this->company->save();
|
||||||
|
|
||||||
|
$tax_data = new TaxData($this->response);
|
||||||
|
|
||||||
$client = Client::factory()->create([
|
$client = Client::factory()->create([
|
||||||
'user_id' => $this->user->id,
|
'user_id' => $this->user->id,
|
||||||
'company_id' => $this->company->id,
|
'company_id' => $this->company->id,
|
||||||
'country_id' => 840,
|
'country_id' => 840,
|
||||||
|
'tax_data' => $tax_data,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$invoice = InvoiceFactory::create($this->company->id, $this->user->id);
|
$invoice = InvoiceFactory::create($this->company->id, $this->user->id);
|
||||||
@ -114,7 +117,7 @@ class SumTaxTest extends TestCase
|
|||||||
|
|
||||||
$line_items = [];
|
$line_items = [];
|
||||||
|
|
||||||
$invoice->tax_data = new TaxData($this->response);
|
$invoice->tax_data = $tax_data;
|
||||||
|
|
||||||
$line_item = new InvoiceItem();
|
$line_item = new InvoiceItem();
|
||||||
$line_item->quantity = 1;
|
$line_item->quantity = 1;
|
||||||
@ -131,7 +134,6 @@ class SumTaxTest extends TestCase
|
|||||||
|
|
||||||
$line_items = $invoice->line_items;
|
$line_items = $invoice->line_items;
|
||||||
|
|
||||||
|
|
||||||
$this->assertEquals(10, $invoice->amount);
|
$this->assertEquals(10, $invoice->amount);
|
||||||
$this->assertEquals("", $line_items[0]->tax_name1);
|
$this->assertEquals("", $line_items[0]->tax_name1);
|
||||||
$this->assertEquals(0, $line_items[0]->tax_rate1);
|
$this->assertEquals(0, $line_items[0]->tax_rate1);
|
||||||
@ -152,19 +154,23 @@ class SumTaxTest extends TestCase
|
|||||||
$this->company->tax_data = $tax_data;
|
$this->company->tax_data = $tax_data;
|
||||||
$this->company->save();
|
$this->company->save();
|
||||||
|
|
||||||
$client = Client::factory()->create([
|
$tax_data = new TaxData($this->response);
|
||||||
|
|
||||||
|
$client = Client::factory()->create([
|
||||||
'user_id' => $this->user->id,
|
'user_id' => $this->user->id,
|
||||||
'company_id' => $this->company->id,
|
'company_id' => $this->company->id,
|
||||||
'country_id' => 840,
|
'country_id' => 840,
|
||||||
]);
|
'tax_data' => $tax_data,
|
||||||
|
]);
|
||||||
|
|
||||||
$invoice = InvoiceFactory::create($this->company->id, $this->user->id);
|
$invoice = InvoiceFactory::create($this->company->id, $this->user->id);
|
||||||
$invoice->client_id = $client->id;
|
$invoice->client_id = $client->id;
|
||||||
$invoice->uses_inclusive_taxes = false;
|
$invoice->uses_inclusive_taxes = false;
|
||||||
|
|
||||||
$line_items = [];
|
$line_items = [];
|
||||||
|
|
||||||
|
$invoice->tax_data = $tax_data;
|
||||||
|
|
||||||
$invoice->tax_data = new TaxData($this->response);
|
|
||||||
|
|
||||||
$line_item = new InvoiceItem;
|
$line_item = new InvoiceItem;
|
||||||
$line_item->quantity = 1;
|
$line_item->quantity = 1;
|
||||||
|
@ -107,6 +107,7 @@ class UsTaxTest extends TestCase
|
|||||||
'shipping_country_id' => 840,
|
'shipping_country_id' => 840,
|
||||||
'has_valid_vat_number' => false,
|
'has_valid_vat_number' => false,
|
||||||
'postal_code' => $postal_code,
|
'postal_code' => $postal_code,
|
||||||
|
'tax_data' => new Response($this->mock_response),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$invoice = Invoice::factory()->create([
|
$invoice = Invoice::factory()->create([
|
||||||
@ -309,6 +310,7 @@ class UsTaxTest extends TestCase
|
|||||||
'shipping_country_id' => 276,
|
'shipping_country_id' => 276,
|
||||||
'has_valid_vat_number' => false,
|
'has_valid_vat_number' => false,
|
||||||
'postal_code' => 'xx',
|
'postal_code' => 'xx',
|
||||||
|
'tax_data' => new Response($this->mock_response),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$invoice = Invoice::factory()->create([
|
$invoice = Invoice::factory()->create([
|
||||||
@ -353,18 +355,18 @@ class UsTaxTest extends TestCase
|
|||||||
{
|
{
|
||||||
|
|
||||||
$invoice = $this->invoiceStub('92582');
|
$invoice = $this->invoiceStub('92582');
|
||||||
$client = $invoice->client;
|
$invoice->client->is_tax_exempt = false;
|
||||||
$client->is_tax_exempt = false;
|
$invoice->client->tax_data = new Response($this->mock_response);
|
||||||
$client->save();
|
|
||||||
|
|
||||||
$company = $invoice->company;
|
$invoice->client->push();
|
||||||
$tax_data = $company->tax_data;
|
|
||||||
|
$tax_data = $invoice->company->tax_data;
|
||||||
|
|
||||||
$tax_data->regions->US->has_sales_above_threshold = true;
|
$tax_data->regions->US->has_sales_above_threshold = true;
|
||||||
$tax_data->regions->US->tax_all_subregions = true;
|
$tax_data->regions->US->tax_all_subregions = true;
|
||||||
|
|
||||||
$company->tax_data = $tax_data;
|
$invoice->company->tax_data = $tax_data;
|
||||||
$company->save();
|
$invoice->company->push();
|
||||||
|
|
||||||
$invoice = $invoice->calc()->getInvoice()->service()->markSent()->save();
|
$invoice = $invoice->calc()->getInvoice()->service()->markSent()->save();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user