mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Fixes for conflicts
This commit is contained in:
commit
be67759902
41
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
41
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: triage
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**What version of Invoice Ninja are you running? ie v4.5.25 / v5.0.30**
|
||||||
|
|
||||||
|
**What environment are you running?**
|
||||||
|
Docker
|
||||||
|
Shared Hosting
|
||||||
|
ZIP
|
||||||
|
Other
|
||||||
|
|
||||||
|
**Have you checked log files (storage/logs/) Please provide redacted output**
|
||||||
|
|
||||||
|
**Have you searched existing issues?**
|
||||||
|
|
||||||
|
**Have you reported this to Slack/forum before posting?**
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**Steps To Reproduce**
|
||||||
|
Please list the steps to reproduce the issue
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
||||||
|
|
||||||
|
<!-- Note: Before posting don't forget to check our "Troubleshooting" category in the [docs](https://invoiceninja.github.io/docs/self-host-troubleshooting/) (https://invoiceninja.github.io/docs/self-host-troubleshooting/) -->
|
||||||
|
|
||||||
|
**(v5) Can you replicate the issue on our demo site? https://demo.invoiceninja.com**
|
24
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
24
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: feature request
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**What version of Invoice Ninja are you running? ie v4.5 / v5**
|
||||||
|
|
||||||
|
**What environment are you running?**
|
||||||
|
Docker
|
||||||
|
Shared Hosting
|
||||||
|
ZIP
|
||||||
|
Other
|
||||||
|
|
||||||
|
**Have you searched existing issues/requests?**
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your request/question.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the request/question here.
|
@ -1 +1 @@
|
|||||||
5.5.49
|
5.5.54
|
@ -119,6 +119,7 @@ class CheckData extends Command
|
|||||||
$this->checkDuplicateRecurringInvoices();
|
$this->checkDuplicateRecurringInvoices();
|
||||||
$this->checkOauthSanity();
|
$this->checkOauthSanity();
|
||||||
$this->checkVendorSettings();
|
$this->checkVendorSettings();
|
||||||
|
$this->checkClientSettings();
|
||||||
|
|
||||||
if(Ninja::isHosted()){
|
if(Ninja::isHosted()){
|
||||||
$this->checkAccountStatuses();
|
$this->checkAccountStatuses();
|
||||||
@ -952,24 +953,24 @@ class CheckData extends Command
|
|||||||
|
|
||||||
if ($this->option('fix') == 'true') {
|
if ($this->option('fix') == 'true') {
|
||||||
|
|
||||||
Client::query()->whereNull('settings->currency_id')->cursor()->each(function ($client){
|
// Client::query()->whereNull('settings->currency_id')->cursor()->each(function ($client){
|
||||||
|
|
||||||
if(is_array($client->settings) && count($client->settings) == 0)
|
// if(is_array($client->settings) && count($client->settings) == 0)
|
||||||
{
|
// {
|
||||||
$settings = ClientSettings::defaults();
|
// $settings = ClientSettings::defaults();
|
||||||
$settings->currency_id = $client->company->settings->currency_id;
|
// $settings->currency_id = $client->company->settings->currency_id;
|
||||||
}
|
// }
|
||||||
else {
|
// else {
|
||||||
$settings = $client->settings;
|
// $settings = $client->settings;
|
||||||
$settings->currency_id = $client->company->settings->currency_id;
|
// $settings->currency_id = $client->company->settings->currency_id;
|
||||||
}
|
// }
|
||||||
|
|
||||||
$client->settings = $settings;
|
// $client->settings = $settings;
|
||||||
$client->save();
|
// $client->save();
|
||||||
|
|
||||||
$this->logMessage("Fixing currency for # {$client->id}");
|
// $this->logMessage("Fixing currency for # {$client->id}");
|
||||||
|
|
||||||
});
|
// });
|
||||||
|
|
||||||
|
|
||||||
Client::query()->whereNull('country_id')->cursor()->each(function ($client){
|
Client::query()->whereNull('country_id')->cursor()->each(function ($client){
|
||||||
|
@ -306,8 +306,8 @@ class CreateSingleAccount extends Command
|
|||||||
|
|
||||||
$webhook_config = [
|
$webhook_config = [
|
||||||
'post_purchase_url' => 'http://ninja.test:8000/api/admin/plan',
|
'post_purchase_url' => 'http://ninja.test:8000/api/admin/plan',
|
||||||
'post_purchase_rest_method' => 'POST',
|
'post_purchase_rest_method' => 'post',
|
||||||
'post_purchase_headers' => [],
|
'post_purchase_headers' => [config('ninja.ninja_hosted_header') => config('ninja.ninja_hosted_secret')],
|
||||||
];
|
];
|
||||||
|
|
||||||
$sub = SubscriptionFactory::create($company->id, $user->id);
|
$sub = SubscriptionFactory::create($company->id, $user->id);
|
||||||
|
@ -22,7 +22,9 @@ use App\Jobs\Ninja\CompanySizeCheck;
|
|||||||
use App\Jobs\Ninja\QueueSize;
|
use App\Jobs\Ninja\QueueSize;
|
||||||
use App\Jobs\Ninja\SystemMaintenance;
|
use App\Jobs\Ninja\SystemMaintenance;
|
||||||
use App\Jobs\Ninja\TaskScheduler;
|
use App\Jobs\Ninja\TaskScheduler;
|
||||||
|
use App\Jobs\Invoice\InvoiceCheckLateWebhook;
|
||||||
use App\Jobs\Quote\QuoteCheckExpired;
|
use App\Jobs\Quote\QuoteCheckExpired;
|
||||||
|
use App\Jobs\Subscription\CleanStaleInvoiceOrder;
|
||||||
use App\Jobs\Util\DiskCleanup;
|
use App\Jobs\Util\DiskCleanup;
|
||||||
use App\Jobs\Util\ReminderJob;
|
use App\Jobs\Util\ReminderJob;
|
||||||
use App\Jobs\Util\SchedulerCheck;
|
use App\Jobs\Util\SchedulerCheck;
|
||||||
@ -68,12 +70,18 @@ class Kernel extends ConsoleKernel
|
|||||||
/* Sends recurring invoices*/
|
/* Sends recurring invoices*/
|
||||||
$schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping()->name('recurring-invoice-job')->onOneServer();
|
$schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping()->name('recurring-invoice-job')->onOneServer();
|
||||||
|
|
||||||
|
/* Stale Invoice Cleanup*/
|
||||||
|
$schedule->job(new CleanStaleInvoiceOrder)->hourly()->withoutOverlapping()->name('stale-invoice-job')->onOneServer();
|
||||||
|
|
||||||
/* Sends recurring invoices*/
|
/* Sends recurring invoices*/
|
||||||
$schedule->job(new RecurringExpensesCron)->dailyAt('00:10')->withoutOverlapping()->name('recurring-expense-job')->onOneServer();
|
$schedule->job(new RecurringExpensesCron)->dailyAt('00:10')->withoutOverlapping()->name('recurring-expense-job')->onOneServer();
|
||||||
|
|
||||||
/* Fires notifications for expired Quotes */
|
/* Fires notifications for expired Quotes */
|
||||||
$schedule->job(new QuoteCheckExpired)->dailyAt('05:10')->withoutOverlapping()->name('quote-expired-job')->onOneServer();
|
$schedule->job(new QuoteCheckExpired)->dailyAt('05:10')->withoutOverlapping()->name('quote-expired-job')->onOneServer();
|
||||||
|
|
||||||
|
/* Fires webhooks for overdue Invoice */
|
||||||
|
$schedule->job(new InvoiceCheckLateWebhook)->dailyAt('07:00')->withoutOverlapping()->name('invoice-overdue-job')->onOneServer();
|
||||||
|
|
||||||
/* Performs auto billing */
|
/* Performs auto billing */
|
||||||
$schedule->job(new AutoBillCron)->dailyAt('06:20')->withoutOverlapping()->name('auto-bill-job')->onOneServer();
|
$schedule->job(new AutoBillCron)->dailyAt('06:20')->withoutOverlapping()->name('auto-bill-job')->onOneServer();
|
||||||
|
|
||||||
|
@ -93,6 +93,10 @@ class ClientRegistrationFields
|
|||||||
'key' => 'vat_number',
|
'key' => 'vat_number',
|
||||||
'required' => false,
|
'required' => false,
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'key' => 'currency_id',
|
||||||
|
'required' => false,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
|
@ -441,7 +441,16 @@ class CompanySettings extends BaseSettings
|
|||||||
|
|
||||||
public $send_email_on_mark_paid = false;
|
public $send_email_on_mark_paid = false;
|
||||||
|
|
||||||
|
public $postmark_secret = '';
|
||||||
|
|
||||||
|
public $mailgun_secret = '';
|
||||||
|
|
||||||
|
public $mailgun_domain = '';
|
||||||
|
|
||||||
public static $casts = [
|
public static $casts = [
|
||||||
|
'postmark_secret' => 'string',
|
||||||
|
'mailgun_secret' => 'string',
|
||||||
|
'mailgun_domain' => 'string',
|
||||||
'send_email_on_mark_paid' => 'bool',
|
'send_email_on_mark_paid' => 'bool',
|
||||||
'vendor_portal_enable_uploads' => 'bool',
|
'vendor_portal_enable_uploads' => 'bool',
|
||||||
'besr_id' => 'string',
|
'besr_id' => 'string',
|
||||||
|
138
app/Export/CSV/ProductSalesExport.php
Normal file
138
app/Export/CSV/ProductSalesExport.php
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Export\CSV;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Client;
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\Credit;
|
||||||
|
use App\Models\Document;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\Product;
|
||||||
|
use App\Transformers\ProductTransformer;
|
||||||
|
use App\Utils\Ninja;
|
||||||
|
use Illuminate\Support\Facades\App;
|
||||||
|
use League\Csv\Writer;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
|
class ProductSalesExport extends BaseExport
|
||||||
|
{
|
||||||
|
private Company $company;
|
||||||
|
|
||||||
|
protected array $input;
|
||||||
|
|
||||||
|
protected $date_key = 'created_at';
|
||||||
|
|
||||||
|
protected array $entity_keys = [
|
||||||
|
'custom_value1' => 'custom_value1',
|
||||||
|
'custom_value2' => 'custom_value2',
|
||||||
|
'custom_value3' => 'custom_value3',
|
||||||
|
'custom_value4' => 'custom_value4',
|
||||||
|
'product_key' => 'product_key',
|
||||||
|
'notes' => 'notes',
|
||||||
|
'cost' => 'cost',
|
||||||
|
'price' => 'price',
|
||||||
|
'quantity' => 'quantity',
|
||||||
|
'tax_rate1' => 'tax_rate1',
|
||||||
|
'tax_rate2' => 'tax_rate2',
|
||||||
|
'tax_rate3' => 'tax_rate3',
|
||||||
|
'tax_name1' => 'tax_name1',
|
||||||
|
'tax_name2' => 'tax_name2',
|
||||||
|
'tax_name3' => 'tax_name3',
|
||||||
|
'is_amount_discount' => 'is_amount_discount',
|
||||||
|
'discount' => 'discount',
|
||||||
|
'line_total' => 'line_total',
|
||||||
|
'gross_line_total' => 'gross_line_total',
|
||||||
|
'status' => 'status',
|
||||||
|
'date' => 'date',
|
||||||
|
'currency' => 'currency',
|
||||||
|
'client' => 'client',
|
||||||
|
];
|
||||||
|
|
||||||
|
private array $decorate_keys = [
|
||||||
|
'client',
|
||||||
|
'currency',
|
||||||
|
'date',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function __construct(Company $company, array $input)
|
||||||
|
{
|
||||||
|
$this->company = $company;
|
||||||
|
$this->input = $input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
MultiDB::setDb($this->company->db);
|
||||||
|
App::forgetInstance('translator');
|
||||||
|
App::setLocale($this->company->locale());
|
||||||
|
$t = app('translator');
|
||||||
|
$t->replace(Ninja::transformTranslations($this->company->settings));
|
||||||
|
|
||||||
|
//load the CSV document from a string
|
||||||
|
$this->csv = Writer::createFromString();
|
||||||
|
|
||||||
|
if (count($this->input['report_keys']) == 0) {
|
||||||
|
$this->input['report_keys'] = array_values($this->entity_keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
//insert the header
|
||||||
|
$this->csv->insertOne($this->buildHeader());
|
||||||
|
|
||||||
|
$query = Invoice::query()
|
||||||
|
->withTrashed()
|
||||||
|
->where('company_id', $this->company->id)
|
||||||
|
->where('is_deleted', 0)
|
||||||
|
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL, Invoice::STATUS_PAID]);
|
||||||
|
|
||||||
|
$query = $this->addDateRange($query);
|
||||||
|
|
||||||
|
$query->cursor()
|
||||||
|
->each(function ($invoice) {
|
||||||
|
|
||||||
|
foreach($invoice->line_items as $item)
|
||||||
|
$this->csv->insertOne($this->buildRow($invoice, $item));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return $this->csv->toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildRow($invoice, $invoice_item) :array
|
||||||
|
{
|
||||||
|
$transformed_entity = (array)$invoice_item;
|
||||||
|
|
||||||
|
$entity = [];
|
||||||
|
|
||||||
|
foreach (array_values($this->input['report_keys']) as $key) {
|
||||||
|
$keyval = array_search($key, $this->entity_keys);
|
||||||
|
|
||||||
|
if (array_key_exists($key, $transformed_entity)) {
|
||||||
|
$entity[$keyval] = $transformed_entity[$key];
|
||||||
|
} else {
|
||||||
|
$entity[$keyval] = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->decorateAdvancedFields($invoice, $entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function decorateAdvancedFields(Invoice $invoice, $entity) :array
|
||||||
|
{
|
||||||
|
$entity['client'] = $invoice->client->present()->name();
|
||||||
|
$entity['currency'] = $invoice->client->currency()->code;
|
||||||
|
$entity['status'] = $invoice->stringStatus($invoice->status_id);
|
||||||
|
$entity['date'] = Carbon::parse($invoice->date)->format($this->company->date_format());
|
||||||
|
|
||||||
|
return $entity;
|
||||||
|
}
|
||||||
|
}
|
@ -77,28 +77,45 @@ class BankTransactionFilters extends QueryFilters
|
|||||||
|
|
||||||
$status_parameters = explode(',', $value);
|
$status_parameters = explode(',', $value);
|
||||||
|
|
||||||
|
$status_array = [];
|
||||||
|
|
||||||
|
$debit_or_withdrawal_array = [];
|
||||||
|
|
||||||
if (in_array('all', $status_parameters)) {
|
if (in_array('all', $status_parameters)) {
|
||||||
return $this->builder;
|
return $this->builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('unmatched', $status_parameters)) {
|
if (in_array('unmatched', $status_parameters)) {
|
||||||
$this->builder->where('status_id', BankTransaction::STATUS_UNMATCHED);
|
$status_array[] = BankTransaction::STATUS_UNMATCHED;
|
||||||
|
// $this->builder->orWhere('status_id', BankTransaction::STATUS_UNMATCHED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('matched', $status_parameters)) {
|
if (in_array('matched', $status_parameters)) {
|
||||||
$this->builder->where('status_id', BankTransaction::STATUS_MATCHED);
|
$status_array[] = BankTransaction::STATUS_MATCHED;
|
||||||
|
// $this->builder->where('status_id', BankTransaction::STATUS_MATCHED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('converted', $status_parameters)) {
|
if (in_array('converted', $status_parameters)) {
|
||||||
$this->builder->where('status_id', BankTransaction::STATUS_CONVERTED);
|
$status_array[] = BankTransaction::STATUS_CONVERTED;
|
||||||
|
// $this->builder->where('status_id', BankTransaction::STATUS_CONVERTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('deposits', $status_parameters)) {
|
if (in_array('deposits', $status_parameters)) {
|
||||||
$this->builder->where('base_type', 'CREDIT');
|
$debit_or_withdrawal_array[] = 'CREDIT';
|
||||||
|
// $this->builder->where('base_type', 'CREDIT');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('withdrawals', $status_parameters)) {
|
if (in_array('withdrawals', $status_parameters)) {
|
||||||
$this->builder->where('base_type', 'DEBIT');
|
$debit_or_withdrawal_array[] = 'DEBIT';
|
||||||
|
// $this->builder->where('base_type', 'DEBIT');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count($status_array) >=1) {
|
||||||
|
$this->builder->whereIn('status_id', $status_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count($debit_or_withdrawal_array) >=1) {
|
||||||
|
$this->builder->orWhereIn('base_type', $debit_or_withdrawal_array);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->builder;
|
return $this->builder;
|
||||||
|
@ -193,9 +193,7 @@ class ClientFilters extends QueryFilters
|
|||||||
->where('clients.company_id', '=', $company_id)
|
->where('clients.company_id', '=', $company_id)
|
||||||
->where('client_contacts.is_primary', '=', true)
|
->where('client_contacts.is_primary', '=', true)
|
||||||
->where('client_contacts.deleted_at', '=', null)
|
->where('client_contacts.deleted_at', '=', null)
|
||||||
//->whereRaw('(clients.name != "" or contacts.first_name != "" or contacts.last_name != "" or contacts.email != "")') // filter out buy now invoices
|
|
||||||
->select(
|
->select(
|
||||||
// DB::raw('COALESCE(clients.currency_id, companies.currency_id) currency_id'),
|
|
||||||
DB::raw('COALESCE(clients.country_id, companies.country_id) country_id'),
|
DB::raw('COALESCE(clients.country_id, companies.country_id) country_id'),
|
||||||
DB::raw("CONCAT(COALESCE(client_contacts.first_name, ''), ' ', COALESCE(client_contacts.last_name, '')) contact"),
|
DB::raw("CONCAT(COALESCE(client_contacts.first_name, ''), ' ', COALESCE(client_contacts.last_name, '')) contact"),
|
||||||
'clients.id',
|
'clients.id',
|
||||||
@ -238,7 +236,6 @@ class ClientFilters extends QueryFilters
|
|||||||
*/
|
*/
|
||||||
public function entityFilter()
|
public function entityFilter()
|
||||||
{
|
{
|
||||||
//return $this->builder->whereCompanyId(auth()->user()->company()->id);
|
|
||||||
return $this->builder->company();
|
return $this->builder->company();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,6 @@ class DesignFilters extends QueryFilters
|
|||||||
$query = DB::table('designs')
|
$query = DB::table('designs')
|
||||||
->join('companies', 'companies.id', '=', 'designs.company_id')
|
->join('companies', 'companies.id', '=', 'designs.company_id')
|
||||||
->where('designs.company_id', '=', $company_id)
|
->where('designs.company_id', '=', $company_id)
|
||||||
//->whereRaw('(designs.name != "" or contacts.first_name != "" or contacts.last_name != "" or contacts.email != "")') // filter out buy now invoices
|
|
||||||
->select(
|
->select(
|
||||||
'designs.id',
|
'designs.id',
|
||||||
'designs.name',
|
'designs.name',
|
||||||
|
@ -69,26 +69,55 @@ class ExpenseFilters extends QueryFilters
|
|||||||
return $this->builder;
|
return $this->builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->builder->whereNested(function ($query) use($status_parameters){
|
||||||
|
|
||||||
if (in_array('logged', $status_parameters)) {
|
if (in_array('logged', $status_parameters)) {
|
||||||
$this->builder->where('amount', '>', 0);
|
|
||||||
|
$query->orWhere(function ($query){
|
||||||
|
$query->where('amount', '>', 0)
|
||||||
|
->whereNull('invoice_id')
|
||||||
|
->whereNull('payment_date');
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('pending', $status_parameters)) {
|
if (in_array('pending', $status_parameters)) {
|
||||||
$this->builder->whereNull('invoice_id')->whereNotNull('payment_date');
|
|
||||||
|
$query->orWhere(function ($query){
|
||||||
|
$query->where('should_be_invoiced',true)
|
||||||
|
->whereNull('invoice_id');
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('invoiced', $status_parameters)) {
|
if (in_array('invoiced', $status_parameters)) {
|
||||||
$this->builder->whereNotNull('invoice_id');
|
|
||||||
|
$query->orWhere(function ($query){
|
||||||
|
$query->whereNotNull('invoice_id');
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('paid', $status_parameters)) {
|
if (in_array('paid', $status_parameters)) {
|
||||||
$this->builder->whereNotNull('payment_date');
|
|
||||||
|
$query->orWhere(function ($query){
|
||||||
|
$query->whereNotNull('payment_date');
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('unpaid', $status_parameters)) {
|
if (in_array('unpaid', $status_parameters)) {
|
||||||
$this->builder->whereNull('payment_date');
|
|
||||||
|
$query->orWhere(function ($query){
|
||||||
|
$query->whereNull('payment_date');
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// nlog($this->builder->toSql());
|
||||||
|
|
||||||
return $this->builder;
|
return $this->builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,9 +205,7 @@ class ExpenseFilters extends QueryFilters
|
|||||||
$query = DB::table('expenses')
|
$query = DB::table('expenses')
|
||||||
->join('companies', 'companies.id', '=', 'expenses.company_id')
|
->join('companies', 'companies.id', '=', 'expenses.company_id')
|
||||||
->where('expenses.company_id', '=', $company_id)
|
->where('expenses.company_id', '=', $company_id)
|
||||||
//->whereRaw('(expenses.name != "" or contacts.first_name != "" or contacts.last_name != "" or contacts.email != "")') // filter out buy now invoices
|
|
||||||
->select(
|
->select(
|
||||||
// DB::raw('COALESCE(expenses.currency_id, companies.currency_id) currency_id'),
|
|
||||||
DB::raw('COALESCE(expenses.country_id, companies.country_id) country_id'),
|
DB::raw('COALESCE(expenses.country_id, companies.country_id) country_id'),
|
||||||
DB::raw("CONCAT(COALESCE(expense_contacts.first_name, ''), ' ', COALESCE(expense_contacts.last_name, '')) contact"),
|
DB::raw("CONCAT(COALESCE(expense_contacts.first_name, ''), ' ', COALESCE(expense_contacts.last_name, '')) contact"),
|
||||||
'expenses.id',
|
'expenses.id',
|
||||||
@ -212,8 +239,6 @@ class ExpenseFilters extends QueryFilters
|
|||||||
*/
|
*/
|
||||||
public function entityFilter()
|
public function entityFilter()
|
||||||
{
|
{
|
||||||
|
|
||||||
//return $this->builder->whereCompanyId(auth()->user()->company()->id);
|
|
||||||
return $this->builder->company();
|
return $this->builder->company();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,20 +47,27 @@ class InvoiceFilters extends QueryFilters
|
|||||||
|
|
||||||
$status_parameters = explode(',', $value);
|
$status_parameters = explode(',', $value);
|
||||||
|
|
||||||
|
$invoice_filters = [];
|
||||||
|
|
||||||
if (in_array('all', $status_parameters)) {
|
if (in_array('all', $status_parameters)) {
|
||||||
return $this->builder;
|
return $this->builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('paid', $status_parameters)) {
|
if (in_array('paid', $status_parameters)) {
|
||||||
$this->builder->where('status_id', Invoice::STATUS_PAID);
|
$invoice_filters[] = Invoice::STATUS_PAID;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('unpaid', $status_parameters)) {
|
if (in_array('unpaid', $status_parameters)) {
|
||||||
$this->builder->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]);
|
$invoice_filters[] = Invoice::STATUS_SENT;
|
||||||
|
$invoice_filters[] = Invoice::STATUS_PARTIAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count($invoice_filters) >0){
|
||||||
|
$this->builder->whereIn('status_id', $invoice_filters);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('overdue', $status_parameters)) {
|
if (in_array('overdue', $status_parameters)) {
|
||||||
$this->builder->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
$this->builder->orWhereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||||
->where('due_date', '<', Carbon::now())
|
->where('due_date', '<', Carbon::now())
|
||||||
->orWhere('partial_due_date', '<', Carbon::now());
|
->orWhere('partial_due_date', '<', Carbon::now());
|
||||||
}
|
}
|
||||||
|
@ -42,20 +42,26 @@ class PurchaseOrderFilters extends QueryFilters
|
|||||||
return $this->builder;
|
return $this->builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$po_status = [];
|
||||||
|
|
||||||
if (in_array('draft', $status_parameters)) {
|
if (in_array('draft', $status_parameters)) {
|
||||||
$this->builder->where('status_id', PurchaseOrder::STATUS_DRAFT);
|
$po_status[] = PurchaseOrder::STATUS_DRAFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('sent', $status_parameters)) {
|
if (in_array('sent', $status_parameters)) {
|
||||||
$this->builder->where('status_id', PurchaseOrder::STATUS_SENT);
|
$po_status[] = PurchaseOrder::STATUS_SENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('accepted', $status_parameters)) {
|
if (in_array('accepted', $status_parameters)) {
|
||||||
$this->builder->where('status_id', PurchaseOrder::STATUS_ACCEPTED);
|
$po_status[] = PurchaseOrder::STATUS_ACCEPTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('cancelled', $status_parameters)) {
|
if (in_array('cancelled', $status_parameters)) {
|
||||||
$this->builder->where('status_id', PurchaseOrder::STATUS_CANCELLED);
|
$po_status[] = PurchaseOrder::STATUS_CANCELLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count($status_parameters) >=1) {
|
||||||
|
$this->builder->whereIn('status_id', $status_parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->builder;
|
return $this->builder;
|
||||||
|
@ -15,6 +15,7 @@ namespace App\Filters;
|
|||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class QueryFilters.
|
* Class QueryFilters.
|
||||||
@ -173,22 +174,30 @@ abstract class QueryFilters
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function created_at($value)
|
public function created_at($value = '')
|
||||||
{
|
{
|
||||||
$created_at = $value ? (int) $value : 0;
|
|
||||||
|
|
||||||
$created_at = date('Y-m-d H:i:s', $value);
|
if($value == '')
|
||||||
|
return $this->builder;
|
||||||
|
|
||||||
if(is_string($created_at)){
|
try{
|
||||||
|
|
||||||
$created_at = strtotime(str_replace("/","-",$created_at));
|
if(is_numeric($value)){
|
||||||
|
$created_at = Carbon::createFromTimestamp((int)$value);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$created_at = Carbon::parse($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->builder->where('created_at', '>=', $created_at);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(\Exception $e) {
|
||||||
|
|
||||||
if(!$created_at)
|
|
||||||
return $this->builder;
|
return $this->builder;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->builder->where('created_at', '>=', $created_at);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function is_deleted($value)
|
public function is_deleted($value)
|
||||||
@ -209,6 +218,15 @@ abstract class QueryFilters
|
|||||||
return $this->builder->where('client_id', $this->decodePrimaryKey($client_id));
|
return $this->builder->where('client_id', $this->decodePrimaryKey($client_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function vendor_id(string $vendor_id = '') :Builder
|
||||||
|
{
|
||||||
|
if (strlen($vendor_id) == 0) {
|
||||||
|
return $this->builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->builder->where('vendor_id', $this->decodePrimaryKey($vendor_id));
|
||||||
|
}
|
||||||
|
|
||||||
public function filter_deleted_clients($value)
|
public function filter_deleted_clients($value)
|
||||||
{
|
{
|
||||||
if ($value == 'true') {
|
if ($value == 'true') {
|
||||||
@ -233,6 +251,7 @@ abstract class QueryFilters
|
|||||||
{
|
{
|
||||||
return $this->builder
|
return $this->builder
|
||||||
->orWhere($this->with_property, $value)
|
->orWhere($this->with_property, $value)
|
||||||
->orderByRaw("{$this->with_property} = ? DESC", [$value]);
|
->orderByRaw("{$this->with_property} = ? DESC", [$value])
|
||||||
|
->company();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,27 +66,38 @@ class QuoteFilters extends QueryFilters
|
|||||||
return $this->builder;
|
return $this->builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$quote_filters = [];
|
||||||
|
|
||||||
if (in_array('draft', $status_parameters)) {
|
if (in_array('draft', $status_parameters)) {
|
||||||
$this->builder->where('status_id', Quote::STATUS_DRAFT);
|
$quote_filters[] = Quote::STATUS_DRAFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('sent', $status_parameters)) {
|
if (in_array('sent', $status_parameters)) {
|
||||||
$this->builder->where('status_id', Quote::STATUS_SENT);
|
$quote_filters[] = Quote::STATUS_SENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('approved', $status_parameters)) {
|
if (in_array('approved', $status_parameters)) {
|
||||||
$this->builder->where('status_id', Quote::STATUS_APPROVED);
|
$quote_filters[] = Quote::STATUS_APPROVED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count($quote_filters) >=1){
|
||||||
|
$this->builder->whereIn('status_id', $quote_filters);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('expired', $status_parameters)) {
|
if (in_array('expired', $status_parameters)) {
|
||||||
$this->builder->where('status_id', Quote::STATUS_SENT)
|
$this->builder->orWhere(function ($query){
|
||||||
->where('due_date', '>=', now()->toDateString());
|
$query->where('status_id', Quote::STATUS_SENT)
|
||||||
|
->whereNotNull('due_date')
|
||||||
|
->where('due_date', '<=', now()->toDateString());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('upcoming', $status_parameters)) {
|
if (in_array('upcoming', $status_parameters)) {
|
||||||
$this->builder->where('status_id', Quote::STATUS_SENT)
|
$this->builder->orWhere(function ($query){
|
||||||
->where('due_date', '<=', now()->toDateString())
|
$query->where('status_id', Quote::STATUS_SENT)
|
||||||
|
->where('due_date', '>=', now()->toDateString())
|
||||||
->orderBy('due_date', 'DESC');
|
->orderBy('due_date', 'DESC');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->builder;
|
return $this->builder;
|
||||||
|
129
app/Filters/SubscriptionFilters.php
Normal file
129
app/Filters/SubscriptionFilters.php
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Filters;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Webhook;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SubscriptionFilters.
|
||||||
|
*/
|
||||||
|
class SubscriptionFilters extends QueryFilters
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Filter based on search text.
|
||||||
|
*
|
||||||
|
* @param string query filter
|
||||||
|
* @return Builder
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
public function filter(string $filter = '') : Builder
|
||||||
|
{
|
||||||
|
if (strlen($filter) == 0) {
|
||||||
|
return $this->builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->builder->where(function ($query) use ($filter) {
|
||||||
|
$query->where('name', 'like', '%'.$filter.'%');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the list based on the status
|
||||||
|
* archived, active, deleted.
|
||||||
|
*
|
||||||
|
* @param string filter
|
||||||
|
* @return Builder
|
||||||
|
*/
|
||||||
|
public function status(string $filter = '') : Builder
|
||||||
|
{
|
||||||
|
if (strlen($filter) == 0) {
|
||||||
|
return $this->builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = 'subscriptions';
|
||||||
|
$filters = explode(',', $filter);
|
||||||
|
|
||||||
|
return $this->builder->where(function ($query) use ($filters, $table) {
|
||||||
|
$query->whereNull($table.'.id');
|
||||||
|
|
||||||
|
if (in_array(parent::STATUS_ACTIVE, $filters)) {
|
||||||
|
$query->orWhereNull($table.'.deleted_at');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array(parent::STATUS_ARCHIVED, $filters)) {
|
||||||
|
$query->orWhere(function ($query) use ($table) {
|
||||||
|
$query->whereNotNull($table.'.deleted_at');
|
||||||
|
|
||||||
|
if (! in_array($table, ['users'])) {
|
||||||
|
$query->where($table.'.is_deleted', '=', 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array(parent::STATUS_DELETED, $filters)) {
|
||||||
|
$query->orWhere($table.'.is_deleted', '=', 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the list based on $sort.
|
||||||
|
*
|
||||||
|
* @param string sort formatted as column|asc
|
||||||
|
* @return Builder
|
||||||
|
*/
|
||||||
|
public function sort(string $sort) : Builder
|
||||||
|
{
|
||||||
|
$sort_col = explode('|', $sort);
|
||||||
|
|
||||||
|
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the base query.
|
||||||
|
*
|
||||||
|
* @param int company_id
|
||||||
|
* @param User $user
|
||||||
|
* @return Builder
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
public function baseQuery(int $company_id, User $user) : Builder
|
||||||
|
{
|
||||||
|
$query = DB::table('subscriptions')
|
||||||
|
->join('companies', 'companies.id', '=', 'subscriptions.company_id')
|
||||||
|
->where('subscriptions.company_id', '=', $company_id);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the user does not have permissions to view all invoices
|
||||||
|
* limit the user to only the invoices they have created
|
||||||
|
*/
|
||||||
|
if (Gate::denies('view-list', Webhook::class)) {
|
||||||
|
$query->where('subscriptions.user_id', '=', $user->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the query by the users company ID.
|
||||||
|
*
|
||||||
|
* @return Illuminate\Database\Query\Builder
|
||||||
|
*/
|
||||||
|
public function entityFilter()
|
||||||
|
{
|
||||||
|
return $this->builder->company();
|
||||||
|
}
|
||||||
|
}
|
@ -104,7 +104,6 @@ class TokenFilters extends QueryFilters
|
|||||||
$query = DB::table('company_tokens')
|
$query = DB::table('company_tokens')
|
||||||
->join('companies', 'companies.id', '=', 'company_tokens.company_id')
|
->join('companies', 'companies.id', '=', 'company_tokens.company_id')
|
||||||
->where('company_tokens.company_id', '=', $company_id)
|
->where('company_tokens.company_id', '=', $company_id)
|
||||||
//->whereRaw('(designs.name != "" or contacts.first_name != "" or contacts.last_name != "" or contacts.email != "")') // filter out buy now invoices
|
|
||||||
->select(
|
->select(
|
||||||
'company_tokens.id',
|
'company_tokens.id',
|
||||||
'company_tokens.name',
|
'company_tokens.name',
|
||||||
|
@ -118,7 +118,6 @@ class VendorFilters extends QueryFilters
|
|||||||
->where('vendors.company_id', '=', $company_id)
|
->where('vendors.company_id', '=', $company_id)
|
||||||
->where('vendor_contacts.is_primary', '=', true)
|
->where('vendor_contacts.is_primary', '=', true)
|
||||||
->where('vendor_contacts.deleted_at', '=', null)
|
->where('vendor_contacts.deleted_at', '=', null)
|
||||||
//->whereRaw('(vendors.name != "" or contacts.first_name != "" or contacts.last_name != "" or contacts.email != "")') // filter out buy now invoices
|
|
||||||
->select(
|
->select(
|
||||||
// DB::raw('COALESCE(vendors.currency_id, companies.currency_id) currency_id'),
|
// DB::raw('COALESCE(vendors.currency_id, companies.currency_id) currency_id'),
|
||||||
DB::raw('COALESCE(vendors.country_id, companies.country_id) country_id'),
|
DB::raw('COALESCE(vendors.country_id, companies.country_id) country_id'),
|
||||||
|
@ -105,7 +105,6 @@ class WebhookFilters extends QueryFilters
|
|||||||
$query = DB::table('webhooks')
|
$query = DB::table('webhooks')
|
||||||
->join('companies', 'companies.id', '=', 'webhooks.company_id')
|
->join('companies', 'companies.id', '=', 'webhooks.company_id')
|
||||||
->where('webhooks.company_id', '=', $company_id)
|
->where('webhooks.company_id', '=', $company_id)
|
||||||
//->whereRaw('(designs.name != "" or contacts.first_name != "" or contacts.last_name != "" or contacts.email != "")') // filter out buy now invoices
|
|
||||||
->select(
|
->select(
|
||||||
'webhooks.id',
|
'webhooks.id',
|
||||||
'webhooks.target_url',
|
'webhooks.target_url',
|
||||||
|
@ -13,6 +13,7 @@ namespace App\Helpers\Epc;
|
|||||||
|
|
||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
|
use App\Models\RecurringInvoice;
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
use BaconQrCode\Renderer\ImageRenderer;
|
use BaconQrCode\Renderer\ImageRenderer;
|
||||||
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
|
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
|
||||||
@ -35,7 +36,7 @@ class EpcQrGenerator
|
|||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct(protected Company $company, protected Invoice $invoice, protected float $amount){}
|
public function __construct(protected Company $company, protected Invoice|RecurringInvoice $invoice, protected float $amount){}
|
||||||
|
|
||||||
public function getQrCode()
|
public function getQrCode()
|
||||||
{
|
{
|
||||||
|
@ -112,6 +112,7 @@ class ActivityController extends BaseController
|
|||||||
'purchase_order' => $activity->purchase_order ? $activity->purchase_order : '',
|
'purchase_order' => $activity->purchase_order ? $activity->purchase_order : '',
|
||||||
'subscription' => $activity->subscription ? $activity->subscription : '',
|
'subscription' => $activity->subscription ? $activity->subscription : '',
|
||||||
'vendor_contact' => $activity->vendor_contact ? $activity->vendor_contact : '',
|
'vendor_contact' => $activity->vendor_contact ? $activity->vendor_contact : '',
|
||||||
|
'recurring_expense' => $activity->recurring_expense ? $activity->recurring_expense : '',
|
||||||
];
|
];
|
||||||
|
|
||||||
return array_merge($arr, $activity->toArray());
|
return array_merge($arr, $activity->toArray());
|
||||||
|
@ -51,6 +51,7 @@ class ContactRegisterController extends Controller
|
|||||||
|
|
||||||
public function register(RegisterRequest $request)
|
public function register(RegisterRequest $request)
|
||||||
{
|
{
|
||||||
|
|
||||||
$request->merge(['company' => $request->company()]);
|
$request->merge(['company' => $request->company()]);
|
||||||
|
|
||||||
$client = $this->getClient($request->all());
|
$client = $this->getClient($request->all());
|
||||||
@ -58,7 +59,7 @@ class ContactRegisterController extends Controller
|
|||||||
|
|
||||||
Auth::guard('contact')->loginUsingId($client_contact->id, true);
|
Auth::guard('contact')->loginUsingId($client_contact->id, true);
|
||||||
|
|
||||||
return redirect()->route('client.dashboard');
|
return redirect()->intended(route('client.dashboard'));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getClient(array $data)
|
private function getClient(array $data)
|
||||||
@ -66,7 +67,15 @@ class ContactRegisterController extends Controller
|
|||||||
$client = ClientFactory::create($data['company']->id, $data['company']->owner()->id);
|
$client = ClientFactory::create($data['company']->id, $data['company']->owner()->id);
|
||||||
|
|
||||||
$client->fill($data);
|
$client->fill($data);
|
||||||
|
|
||||||
$client->save();
|
$client->save();
|
||||||
|
|
||||||
|
if(isset($data['currency_id'])) {
|
||||||
|
$settings = $client->settings;
|
||||||
|
$settings->currency_id = isset($data['currency_id']) ? $data['currency_id'] : $data['company']->settings->currency_id;
|
||||||
|
$client->settings = $settings;
|
||||||
|
}
|
||||||
|
|
||||||
$client->number = $this->getNextClientNumber($client);
|
$client->number = $this->getNextClientNumber($client);
|
||||||
$client->save();
|
$client->save();
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ class InvoiceController extends Controller
|
|||||||
*
|
*
|
||||||
* @return Factory|View
|
* @return Factory|View
|
||||||
*/
|
*/
|
||||||
public function show(ShowInvoiceRequest $request, Invoice $invoice)
|
public function show(ShowInvoiceRequest $request, Invoice $invoice, ?string $hash = null)
|
||||||
{
|
{
|
||||||
set_time_limit(0);
|
set_time_limit(0);
|
||||||
|
|
||||||
@ -69,6 +69,7 @@ class InvoiceController extends Controller
|
|||||||
'invoice' => $invoice,
|
'invoice' => $invoice,
|
||||||
'invitation' => $invitation ?: $invoice->invitations->first(),
|
'invitation' => $invitation ?: $invoice->invitations->first(),
|
||||||
'key' => $invitation ? $invitation->key : false,
|
'key' => $invitation ? $invitation->key : false,
|
||||||
|
'hash' => $hash,
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($request->query('mode') === 'fullscreen') {
|
if ($request->query('mode') === 'fullscreen') {
|
||||||
|
@ -26,6 +26,7 @@ use App\Models\Invoice;
|
|||||||
use App\Models\RecurringInvoice;
|
use App\Models\RecurringInvoice;
|
||||||
use App\Models\Subscription;
|
use App\Models\Subscription;
|
||||||
use App\Notifications\Ninja\NewAccountNotification;
|
use App\Notifications\Ninja\NewAccountNotification;
|
||||||
|
use App\Repositories\RecurringInvoiceRepository;
|
||||||
use App\Repositories\SubscriptionRepository;
|
use App\Repositories\SubscriptionRepository;
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
@ -178,6 +179,15 @@ class NinjaPlanController extends Controller
|
|||||||
->increment()
|
->increment()
|
||||||
->queue();
|
->queue();
|
||||||
|
|
||||||
|
|
||||||
|
$old_recurring = RecurringInvoice::where('company_id', config('ninja.ninja_default_company_id'))->where('client_id', $client->id)->first();
|
||||||
|
|
||||||
|
if($old_recurring) {
|
||||||
|
$old_recurring->service()->stop()->save();
|
||||||
|
$old_recurring_repo = new RecurringInvoiceRepository();
|
||||||
|
$old_recurring_repo->archive($old_recurring);
|
||||||
|
}
|
||||||
|
|
||||||
$ninja_company = Company::on('db-ninja-01')->find(config('ninja.ninja_default_company_id'));
|
$ninja_company = Company::on('db-ninja-01')->find(config('ninja.ninja_default_company_id'));
|
||||||
$ninja_company->notification(new NewAccountNotification($subscription->company->account, $client))->ninja();
|
$ninja_company->notification(new NewAccountNotification($subscription->company->account, $client))->ninja();
|
||||||
|
|
||||||
|
@ -148,8 +148,17 @@ class PaymentController extends Controller
|
|||||||
|
|
||||||
$payment = $payment->service()->applyCredits($payment_hash)->save();
|
$payment = $payment->service()->applyCredits($payment_hash)->save();
|
||||||
|
|
||||||
|
$invoices = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')));
|
||||||
|
|
||||||
event('eloquent.created: App\Models\Payment', $payment);
|
event('eloquent.created: App\Models\Payment', $payment);
|
||||||
|
|
||||||
|
if($invoices->sum('balance') > 0){
|
||||||
|
|
||||||
|
$invoice = $invoices->first();
|
||||||
|
|
||||||
|
return redirect()->route('client.invoice.show', ['invoice' => $invoice->hashed_id, 'hash' => $request->input('hash')]);
|
||||||
|
}
|
||||||
|
|
||||||
if (property_exists($payment_hash->data, 'billing_context')) {
|
if (property_exists($payment_hash->data, 'billing_context')) {
|
||||||
$billing_subscription = \App\Models\Subscription::find($payment_hash->data->billing_context->subscription_id);
|
$billing_subscription = \App\Models\Subscription::find($payment_hash->data->billing_context->subscription_id);
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
namespace App\Http\Controllers\ClientPortal;
|
namespace App\Http\Controllers\ClientPortal;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\ClientPortal\RecurringInvoices\ShowRecurringInvoiceRequest;
|
||||||
use App\Models\RecurringInvoice;
|
use App\Models\RecurringInvoice;
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -38,4 +39,20 @@ class SubscriptionController extends Controller
|
|||||||
|
|
||||||
return render('subscriptions.index');
|
return render('subscriptions.index');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the recurring invoice.
|
||||||
|
*
|
||||||
|
* @param ShowRecurringInvoiceRequest $request
|
||||||
|
* @param RecurringInvoice $recurring_invoice
|
||||||
|
* @return Factory|View
|
||||||
|
*/
|
||||||
|
public function show(ShowRecurringInvoiceRequest $request, RecurringInvoice $recurring_invoice)
|
||||||
|
{
|
||||||
|
return $this->render('subscriptions.show', [
|
||||||
|
'invoice' => $recurring_invoice->load('invoices','subscription'),
|
||||||
|
'subscription' => $recurring_invoice->subscription
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,9 +31,12 @@ class SubscriptionPlanSwitchController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(ShowPlanSwitchRequest $request, RecurringInvoice $recurring_invoice, Subscription $target)
|
public function index(ShowPlanSwitchRequest $request, RecurringInvoice $recurring_invoice, Subscription $target)
|
||||||
{
|
{
|
||||||
|
|
||||||
$amount = $recurring_invoice->subscription
|
$amount = $recurring_invoice->subscription
|
||||||
->service()
|
->service()
|
||||||
->calculateUpgradePrice($recurring_invoice, $target);
|
->calculateUpgradePriceV2($recurring_invoice, $target);
|
||||||
|
|
||||||
|
nlog("payment amount = {$amount}");
|
||||||
/**
|
/**
|
||||||
* Null value here is a proxy for
|
* Null value here is a proxy for
|
||||||
* denying the user a change plan option
|
* denying the user a change plan option
|
||||||
@ -42,11 +45,14 @@ class SubscriptionPlanSwitchController extends Controller
|
|||||||
render('subscriptions.denied');
|
render('subscriptions.denied');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$amount = max(0,$amount);
|
||||||
|
|
||||||
return render('subscriptions.switch', [
|
return render('subscriptions.switch', [
|
||||||
'subscription' => $recurring_invoice->subscription,
|
'subscription' => $recurring_invoice->subscription,
|
||||||
'recurring_invoice' => $recurring_invoice,
|
'recurring_invoice' => $recurring_invoice,
|
||||||
'target' => $target,
|
'target' => $target,
|
||||||
'amount' => $amount,
|
'amount' => $amount,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,16 @@ class SubscriptionPurchaseController extends Controller
|
|||||||
$this->setLocale($request->query('locale'));
|
$this->setLocale($request->query('locale'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!auth()->guard('contact')->check() && $subscription->registration_required && $subscription->company->client_can_register) {
|
||||||
|
|
||||||
|
session()->put('url.intended', route('client.subscription.upgrade',['subscription' => $subscription->hashed_id]));
|
||||||
|
|
||||||
|
return redirect()->route('client.register', ['company_key' => $subscription->company->company_key]);
|
||||||
|
}
|
||||||
|
elseif(!auth()->guard('contact')->check() && $subscription->registration_required && ! $subscription->company->client_can_register) {
|
||||||
|
return render('generic.subscription_blocked', ['account' => $subscription->company->account, 'company' => $subscription->company]);
|
||||||
|
}
|
||||||
|
|
||||||
return view('billing-portal.purchasev2', [
|
return view('billing-portal.purchasev2', [
|
||||||
'subscription' => $subscription,
|
'subscription' => $subscription,
|
||||||
'hash' => Str::uuid()->toString(),
|
'hash' => Str::uuid()->toString(),
|
||||||
|
@ -521,7 +521,7 @@ class CompanyController extends BaseController
|
|||||||
$nmo->company = $other_company;
|
$nmo->company = $other_company;
|
||||||
$nmo->settings = $other_company->settings;
|
$nmo->settings = $other_company->settings;
|
||||||
$nmo->to_user = auth()->user();
|
$nmo->to_user = auth()->user();
|
||||||
NinjaMailerJob::dispatch($nmo, true);
|
(new NinjaMailerJob($nmo, true))->handle();
|
||||||
|
|
||||||
$company->delete();
|
$company->delete();
|
||||||
|
|
||||||
|
@ -135,11 +135,45 @@ class ExpenseCategoryController extends BaseController
|
|||||||
return $this->itemResponse($expense_category);
|
return $this->itemResponse($expense_category);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a newly created resource in storage.
|
* Store a newly created resource in storage.
|
||||||
*
|
*
|
||||||
* @param StoreExpenseCategoryRequest $request
|
* @param StoreInvoiceRequest $request The request
|
||||||
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @OA\Post(
|
||||||
|
* path="/api/v1/expense_categories",
|
||||||
|
* operationId="storeExpenseCategory",
|
||||||
|
* tags={"expense_categories"},
|
||||||
|
* summary="Adds a expense category",
|
||||||
|
* description="Adds an expense category to the system",
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||||
|
* @OA\Response(
|
||||||
|
* response=200,
|
||||||
|
* description="Returns the saved invoice object",
|
||||||
|
* @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\JsonContent(ref="#/components/schemas/ExpenseCategory"),
|
||||||
|
* ),
|
||||||
|
* @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 store(StoreExpenseCategoryRequest $request)
|
public function store(StoreExpenseCategoryRequest $request)
|
||||||
{
|
{
|
||||||
|
@ -13,7 +13,6 @@ namespace App\Http\Controllers;
|
|||||||
|
|
||||||
use App\Http\Requests\Import\ImportRequest;
|
use App\Http\Requests\Import\ImportRequest;
|
||||||
use App\Http\Requests\Import\PreImportRequest;
|
use App\Http\Requests\Import\PreImportRequest;
|
||||||
use App\Jobs\Import\CSVImport;
|
|
||||||
use App\Jobs\Import\CSVIngest;
|
use App\Jobs\Import\CSVIngest;
|
||||||
use Illuminate\Http\UploadedFile;
|
use Illuminate\Http\UploadedFile;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
@ -162,7 +161,7 @@ class ImportController extends Controller
|
|||||||
public function detectDelimiter($csvfile)
|
public function detectDelimiter($csvfile)
|
||||||
{
|
{
|
||||||
$delimiters = array(',', '.', ';');
|
$delimiters = array(',', '.', ';');
|
||||||
$bestDelimiter = false;
|
$bestDelimiter = ' ';
|
||||||
$count = 0;
|
$count = 0;
|
||||||
foreach ($delimiters as $delimiter)
|
foreach ($delimiters as $delimiter)
|
||||||
if (substr_count($csvfile, $delimiter) > $count) {
|
if (substr_count($csvfile, $delimiter) > $count) {
|
||||||
|
@ -18,6 +18,10 @@ class PaymentWebhookController extends Controller
|
|||||||
{
|
{
|
||||||
public function __invoke(PaymentWebhookRequest $request)
|
public function __invoke(PaymentWebhookRequest $request)
|
||||||
{
|
{
|
||||||
|
//return early if we cannot resolve the company gateway
|
||||||
|
if(!$request->getCompanyGateway())
|
||||||
|
return response()->json([], 200);
|
||||||
|
|
||||||
return $request
|
return $request
|
||||||
->getCompanyGateway()
|
->getCompanyGateway()
|
||||||
->driver()
|
->driver()
|
||||||
|
@ -74,7 +74,7 @@ class PostMarkController extends BaseController
|
|||||||
*/
|
*/
|
||||||
public function webhook(Request $request)
|
public function webhook(Request $request)
|
||||||
{
|
{
|
||||||
if ($request->header('X-API-SECURITY') && $request->header('X-API-SECURITY') == config('postmark.secret')) {
|
if ($request->header('X-API-SECURITY') && $request->header('X-API-SECURITY') == config('services.postmark.token')) {
|
||||||
ProcessPostmarkWebhook::dispatch($request->all());
|
ProcessPostmarkWebhook::dispatch($request->all());
|
||||||
|
|
||||||
return response()->json(['message' => 'Success'], 200);
|
return response()->json(['message' => 'Success'], 200);
|
||||||
|
@ -198,7 +198,7 @@ class PurchaseOrderController extends BaseController
|
|||||||
|
|
||||||
event(new PurchaseOrderWasCreated($purchase_order, $purchase_order->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
event(new PurchaseOrderWasCreated($purchase_order, $purchase_order->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||||
|
|
||||||
return $this->itemResponse($purchase_order);
|
return $this->itemResponse($purchase_order->fresh());
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Display the specified resource.
|
* Display the specified resource.
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Reports;
|
||||||
|
|
||||||
|
use App\Export\CSV\ProductExport;
|
||||||
|
use App\Export\CSV\ProductSalesExport;
|
||||||
|
use App\Http\Controllers\BaseController;
|
||||||
|
use App\Http\Requests\Report\GenericReportRequest;
|
||||||
|
use App\Http\Requests\Report\ProductSalesReportRequest;
|
||||||
|
use App\Jobs\Report\SendToAdmin;
|
||||||
|
use App\Models\Client;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Http\Response;
|
||||||
|
|
||||||
|
class ProductSalesReportController extends BaseController
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
|
||||||
|
private string $filename = 'product_sales.csv';
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @OA\Post(
|
||||||
|
* path="/api/v1/reports/product_sales",
|
||||||
|
* operationId="getProductSalesReport",
|
||||||
|
* tags={"reports"},
|
||||||
|
* summary="Product Salesreports",
|
||||||
|
* description="Export product sales reports",
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||||
|
* @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(ProductSalesReportRequest $request)
|
||||||
|
{
|
||||||
|
if ($request->has('send_email') && $request->get('send_email')) {
|
||||||
|
SendToAdmin::dispatch(auth()->user()->company(), $request->all(), ProductSalesExport::class, $this->filename);
|
||||||
|
|
||||||
|
return response()->json(['message' => 'working...'], 200);
|
||||||
|
}
|
||||||
|
// expect a list of visible fields, or use the default
|
||||||
|
|
||||||
|
$export = new ProductSalesExport(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);
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@ namespace App\Http\Controllers;
|
|||||||
use App\Events\Subscription\SubscriptionWasCreated;
|
use App\Events\Subscription\SubscriptionWasCreated;
|
||||||
use App\Events\Subscription\SubscriptionWasUpdated;
|
use App\Events\Subscription\SubscriptionWasUpdated;
|
||||||
use App\Factory\SubscriptionFactory;
|
use App\Factory\SubscriptionFactory;
|
||||||
|
use App\Filters\SubscriptionFilters;
|
||||||
use App\Http\Requests\Subscription\CreateSubscriptionRequest;
|
use App\Http\Requests\Subscription\CreateSubscriptionRequest;
|
||||||
use App\Http\Requests\Subscription\DestroySubscriptionRequest;
|
use App\Http\Requests\Subscription\DestroySubscriptionRequest;
|
||||||
use App\Http\Requests\Subscription\EditSubscriptionRequest;
|
use App\Http\Requests\Subscription\EditSubscriptionRequest;
|
||||||
@ -80,9 +81,9 @@ class SubscriptionController extends BaseController
|
|||||||
* ),
|
* ),
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
public function index(): \Illuminate\Http\Response
|
public function index(SubscriptionFilters $filters): \Illuminate\Http\Response
|
||||||
{
|
{
|
||||||
$subscriptions = Subscription::query()->company();
|
$subscriptions = Subscription::filter($filters);
|
||||||
|
|
||||||
return $this->listResponse($subscriptions);
|
return $this->listResponse($subscriptions);
|
||||||
}
|
}
|
||||||
|
@ -174,6 +174,8 @@ class BillingPortalPurchase extends Component
|
|||||||
*/
|
*/
|
||||||
public $company;
|
public $company;
|
||||||
|
|
||||||
|
public $db;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Campaign reference.
|
* Campaign reference.
|
||||||
*
|
*
|
||||||
@ -183,7 +185,11 @@ class BillingPortalPurchase extends Component
|
|||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
MultiDB::setDb($this->company->db);
|
MultiDB::setDb($this->db);
|
||||||
|
|
||||||
|
$this->subscription = Subscription::with('company')->find($this->subscription);
|
||||||
|
|
||||||
|
$this->company = $this->subscription->company;
|
||||||
|
|
||||||
$this->quantity = 1;
|
$this->quantity = 1;
|
||||||
|
|
||||||
@ -225,10 +231,10 @@ class BillingPortalPurchase extends Component
|
|||||||
|
|
||||||
$this->steps['existing_user'] = false;
|
$this->steps['existing_user'] = false;
|
||||||
|
|
||||||
$contact = $this->createBlankClient();
|
$this->contact = $this->createBlankClient();
|
||||||
|
|
||||||
if ($contact && $contact instanceof ClientContact) {
|
if ($this->contact && $this->contact instanceof ClientContact) {
|
||||||
$this->getPaymentMethods($contact);
|
$this->getPaymentMethods($this->contact);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,9 +271,6 @@ class BillingPortalPurchase extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// nlog($this->subscription->group_settings->settings);
|
|
||||||
// nlog($this->subscription->group_settings->settings->currency_id);
|
|
||||||
|
|
||||||
if(array_key_exists('currency_id', $this->request_data)) {
|
if(array_key_exists('currency_id', $this->request_data)) {
|
||||||
|
|
||||||
$currency = Cache::get('currencies')->filter(function ($item){
|
$currency = Cache::get('currencies')->filter(function ($item){
|
||||||
|
@ -15,6 +15,7 @@ use App\DataMapper\ClientSettings;
|
|||||||
use App\Factory\ClientFactory;
|
use App\Factory\ClientFactory;
|
||||||
use App\Jobs\Mail\NinjaMailerJob;
|
use App\Jobs\Mail\NinjaMailerJob;
|
||||||
use App\Jobs\Mail\NinjaMailerObject;
|
use App\Jobs\Mail\NinjaMailerObject;
|
||||||
|
use App\Jobs\Subscription\CleanStaleInvoiceOrder;
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
use App\Mail\ContactPasswordlessLogin;
|
use App\Mail\ContactPasswordlessLogin;
|
||||||
use App\Mail\Subscription\OtpCode;
|
use App\Mail\Subscription\OtpCode;
|
||||||
@ -120,7 +121,7 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
public $request_data;
|
public $request_data = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instance of company.
|
* Instance of company.
|
||||||
@ -129,6 +130,14 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
*/
|
*/
|
||||||
public $company;
|
public $company;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance of company.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public string $db;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Campaign reference.
|
* Campaign reference.
|
||||||
*
|
*
|
||||||
@ -151,10 +160,23 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
public $valid_coupon = false;
|
public $valid_coupon = false;
|
||||||
public $payable_invoices = [];
|
public $payable_invoices = [];
|
||||||
public $payment_confirmed = false;
|
public $payment_confirmed = false;
|
||||||
|
public $is_eligible = true;
|
||||||
|
public $not_eligible_message = '';
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
MultiDB::setDb($this->company->db);
|
MultiDB::setDb($this->db);
|
||||||
|
|
||||||
|
$this->subscription = Subscription::with('company')->find($this->subscription);
|
||||||
|
|
||||||
|
$this->company = $this->subscription->company;
|
||||||
|
|
||||||
|
if(auth()->guard('contact')->check()){
|
||||||
|
$this->email = auth()->guard('contact')->user()->email;
|
||||||
|
$this->contact = auth()->guard('contact')->user();
|
||||||
|
$this->authenticated = true;
|
||||||
|
$this->payment_started = true;
|
||||||
|
}
|
||||||
|
|
||||||
$this->discount = 0;
|
$this->discount = 0;
|
||||||
$this->sub_total = 0;
|
$this->sub_total = 0;
|
||||||
@ -177,7 +199,7 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
$this->coupon = request()->query('coupon');
|
$this->coupon = request()->query('coupon');
|
||||||
$this->handleCoupon();
|
$this->handleCoupon();
|
||||||
}
|
}
|
||||||
elseif(strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0){
|
elseif(isset($this->subscription->promo_code) && strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0){
|
||||||
$this->price = $this->subscription->promo_price;
|
$this->price = $this->subscription->promo_price;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,6 +246,8 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
|
|
||||||
public function resetEmail()
|
public function resetEmail()
|
||||||
{
|
{
|
||||||
|
$this->resetErrorBag('login');
|
||||||
|
$this->resetValidation('login');
|
||||||
$this->email = null;
|
$this->email = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -449,8 +473,6 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
|
|
||||||
$this->buildBundle();
|
$this->buildBundle();
|
||||||
|
|
||||||
nlog($this->bundle);
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -489,9 +511,20 @@ nlog($this->bundle);
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function handleBeforePaymentEvents() :void
|
public function handleBeforePaymentEvents() :self
|
||||||
{
|
{
|
||||||
|
|
||||||
|
$eligibility_check = $this->subscription->service()->isEligible($this->contact);
|
||||||
|
|
||||||
|
if(is_array($eligibility_check) && $eligibility_check['message'] != 'Success'){
|
||||||
|
|
||||||
|
$this->is_eligible = false;
|
||||||
|
$this->not_eligible_message = $eligibility_check['message'];
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'client_id' => $this->contact->client->id,
|
'client_id' => $this->contact->client->id,
|
||||||
'date' => now()->format('Y-m-d'),
|
'date' => now()->format('Y-m-d'),
|
||||||
@ -501,19 +534,9 @@ nlog($this->bundle);
|
|||||||
]],
|
]],
|
||||||
'user_input_promo_code' => $this->coupon,
|
'user_input_promo_code' => $this->coupon,
|
||||||
'coupon' => empty($this->subscription->promo_code) ? '' : $this->coupon,
|
'coupon' => empty($this->subscription->promo_code) ? '' : $this->coupon,
|
||||||
// 'quantity' => $this->quantity,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$is_eligible = $this->subscription->service()->isEligible($this->contact);
|
|
||||||
|
|
||||||
// if (is_array($is_eligible) && $is_eligible['message'] != 'Success') {
|
|
||||||
// $this->steps['not_eligible'] = true;
|
|
||||||
// $this->steps['not_eligible_message'] = $is_eligible['message'];
|
|
||||||
// $this->steps['show_loading_bar'] = false;
|
|
||||||
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
$this->invoice = $this->subscription
|
$this->invoice = $this->subscription
|
||||||
->service()
|
->service()
|
||||||
->createInvoiceV2($this->bundle, $this->contact->client_id, $this->valid_coupon)
|
->createInvoiceV2($this->bundle, $this->contact->client_id, $this->valid_coupon)
|
||||||
@ -534,6 +557,9 @@ nlog($this->bundle);
|
|||||||
], now()->addMinutes(60));
|
], now()->addMinutes(60));
|
||||||
|
|
||||||
$this->emit('beforePaymentEventsCompleted');
|
$this->emit('beforePaymentEventsCompleted');
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleTrial()
|
public function handleTrial()
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
namespace App\Http\Livewire;
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Company;
|
||||||
use App\Models\Credit;
|
use App\Models\Credit;
|
||||||
use App\Utils\Traits\WithSorting;
|
use App\Utils\Traits\WithSorting;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@ -23,26 +24,31 @@ class CreditsTable extends Component
|
|||||||
use WithPagination;
|
use WithPagination;
|
||||||
use WithSorting;
|
use WithSorting;
|
||||||
|
|
||||||
public $per_page = 10;
|
public int $per_page = 10;
|
||||||
|
|
||||||
public $company;
|
public Company $company;
|
||||||
|
|
||||||
|
public string $db;
|
||||||
|
|
||||||
|
public int $company_id;
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
MultiDB::setDb($this->company->db);
|
MultiDB::setDb($this->db);
|
||||||
|
|
||||||
|
$this->company = Company::find($this->company_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
$query = Credit::query()
|
$query = Credit::query()
|
||||||
->where('client_id', auth()->guard('contact')->user()->client_id)
|
|
||||||
->where('company_id', $this->company->id)
|
->where('company_id', $this->company->id)
|
||||||
|
->where('client_id', auth()->guard('contact')->user()->client_id)
|
||||||
->where('status_id', '<>', Credit::STATUS_DRAFT)
|
->where('status_id', '<>', Credit::STATUS_DRAFT)
|
||||||
->where('is_deleted', 0)
|
->where('is_deleted', 0)
|
||||||
->where(function ($query) {
|
->where(function ($query) {
|
||||||
$query->whereDate('due_date', '>=', now())
|
$query->whereDate('due_date', '>=', now())
|
||||||
->orWhereNull('due_date');
|
->orWhereNull('due_date');
|
||||||
//->orWhere('due_date', '=', '');
|
|
||||||
})
|
})
|
||||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||||
->withTrashed()
|
->withTrashed()
|
||||||
|
@ -14,6 +14,7 @@ namespace App\Http\Livewire;
|
|||||||
|
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
|
use App\Models\Company;
|
||||||
use App\Models\Credit;
|
use App\Models\Credit;
|
||||||
use App\Models\Document;
|
use App\Models\Document;
|
||||||
use App\Models\Expense;
|
use App\Models\Expense;
|
||||||
@ -31,21 +32,27 @@ class DocumentsTable extends Component
|
|||||||
{
|
{
|
||||||
use WithPagination, WithSorting;
|
use WithPagination, WithSorting;
|
||||||
|
|
||||||
public $client;
|
public Company $company;
|
||||||
|
|
||||||
public $per_page = 10;
|
public Client $client;
|
||||||
|
|
||||||
public $company;
|
public int $client_id;
|
||||||
|
|
||||||
|
public int $per_page = 10;
|
||||||
|
|
||||||
public string $tab = 'documents';
|
public string $tab = 'documents';
|
||||||
|
|
||||||
|
public string $db;
|
||||||
|
|
||||||
protected $query;
|
protected $query;
|
||||||
|
|
||||||
public function mount($client)
|
public function mount()
|
||||||
{
|
{
|
||||||
MultiDB::setDb($this->company->db);
|
MultiDB::setDb($this->db);
|
||||||
|
|
||||||
$this->client = $client;
|
$this->client = Client::with('company')->find($this->client_id);
|
||||||
|
|
||||||
|
$this->company = $this->client->company;
|
||||||
|
|
||||||
$this->query = $this->documents();
|
$this->query = $this->documents();
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
namespace App\Http\Livewire;
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Company;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Utils\Traits\WithSorting;
|
use App\Utils\Traits\WithSorting;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
@ -23,15 +24,21 @@ class InvoicesTable extends Component
|
|||||||
{
|
{
|
||||||
use WithPagination, WithSorting;
|
use WithPagination, WithSorting;
|
||||||
|
|
||||||
public $per_page = 10;
|
public int $per_page = 10;
|
||||||
|
|
||||||
public $status = [];
|
public array $status = [];
|
||||||
|
|
||||||
public $company;
|
public Company $company;
|
||||||
|
|
||||||
|
public int $company_id;
|
||||||
|
|
||||||
|
public string $db;
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
MultiDB::setDb($this->company->db);
|
MultiDB::setDb($this->db);
|
||||||
|
|
||||||
|
$this->company = Company::find($this->company_id);
|
||||||
|
|
||||||
$this->sort_asc = false;
|
$this->sort_asc = false;
|
||||||
|
|
||||||
|
@ -23,13 +23,11 @@ class PayNowDropdown extends Component
|
|||||||
|
|
||||||
public $company;
|
public $company;
|
||||||
|
|
||||||
public function mount(int $total)
|
public function mount()
|
||||||
{
|
{
|
||||||
MultiDB::setDb($this->company->db);
|
MultiDB::setDb($this->company->db);
|
||||||
|
|
||||||
$this->total = $total;
|
$this->methods = auth()->guard('contact')->user()->client->service()->getPaymentMethods($this->total);
|
||||||
|
|
||||||
$this->methods = auth()->guard('contact')->user()->client->service()->getPaymentMethods($total);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
|
@ -3,7 +3,9 @@
|
|||||||
namespace App\Http\Livewire;
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Client;
|
||||||
use App\Models\ClientGatewayToken;
|
use App\Models\ClientGatewayToken;
|
||||||
|
use App\Models\Company;
|
||||||
use App\Utils\Traits\WithSorting;
|
use App\Utils\Traits\WithSorting;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Livewire\WithPagination;
|
use Livewire\WithPagination;
|
||||||
@ -13,17 +15,23 @@ class PaymentMethodsTable extends Component
|
|||||||
use WithPagination;
|
use WithPagination;
|
||||||
use WithSorting;
|
use WithSorting;
|
||||||
|
|
||||||
public $per_page = 10;
|
public int $per_page = 10;
|
||||||
|
|
||||||
public $client;
|
public Client $client;
|
||||||
|
|
||||||
public $company;
|
public Company $company;
|
||||||
|
|
||||||
public function mount($client)
|
public int $client_id;
|
||||||
|
|
||||||
|
public string $db;
|
||||||
|
|
||||||
|
public function mount()
|
||||||
{
|
{
|
||||||
MultiDB::setDb($this->company->db);
|
MultiDB::setDb($this->db);
|
||||||
|
|
||||||
$this->client = $client;
|
$this->client = Client::with('company')->find($this->client_id);
|
||||||
|
|
||||||
|
$this->company = $this->client->company;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
namespace App\Http\Livewire;
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Company;
|
||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
use App\Utils\Traits\WithSorting;
|
use App\Utils\Traits\WithSorting;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@ -23,17 +24,19 @@ class PaymentsTable extends Component
|
|||||||
use WithSorting;
|
use WithSorting;
|
||||||
use WithPagination;
|
use WithPagination;
|
||||||
|
|
||||||
public $per_page = 10;
|
public int $per_page = 10;
|
||||||
|
|
||||||
public $user;
|
public Company $company;
|
||||||
|
|
||||||
public $company;
|
public int $company_id;
|
||||||
|
|
||||||
|
public string $db;
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
MultiDB::setDb($this->company->db);
|
MultiDB::setDb($this->db);
|
||||||
|
|
||||||
$this->user = auth()->user();
|
$this->company = Company::find($this->company_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
namespace App\Http\Livewire;
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Company;
|
||||||
use App\Models\Quote;
|
use App\Models\Quote;
|
||||||
use App\Utils\Traits\WithSorting;
|
use App\Utils\Traits\WithSorting;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@ -22,15 +23,27 @@ class QuotesTable extends Component
|
|||||||
{
|
{
|
||||||
use WithPagination;
|
use WithPagination;
|
||||||
|
|
||||||
public $per_page = 10;
|
public int $per_page = 10;
|
||||||
|
|
||||||
public $status = [];
|
public array $status = [];
|
||||||
|
|
||||||
public $company;
|
public Company $company;
|
||||||
|
|
||||||
public $sort = 'status_id'; // Default sortBy. Feel free to change or pull from client/company settings.
|
public string $sort = 'status_id';
|
||||||
|
|
||||||
|
public bool $sort_asc = true;
|
||||||
|
|
||||||
|
public int $company_id;
|
||||||
|
|
||||||
|
public string $db;
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
MultiDB::setDb($this->db);
|
||||||
|
|
||||||
|
$this->company = Company::find($this->company_id);
|
||||||
|
}
|
||||||
|
|
||||||
public $sort_asc = true;
|
|
||||||
|
|
||||||
public function sortBy($field)
|
public function sortBy($field)
|
||||||
{
|
{
|
||||||
@ -41,16 +54,11 @@ class QuotesTable extends Component
|
|||||||
$this->sort = $field;
|
$this->sort = $field;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
MultiDB::setDb($this->company->db);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
|
|
||||||
$query = Quote::query()
|
$query = Quote::query()
|
||||||
->with('client.gateway_tokens', 'company', 'client.contacts')
|
->with('client.contacts', 'company')
|
||||||
->orderBy($this->sort, $this->sort_asc ? 'asc' : 'desc');
|
->orderBy($this->sort, $this->sort_asc ? 'asc' : 'desc');
|
||||||
|
|
||||||
if (count($this->status) > 0) {
|
if (count($this->status) > 0) {
|
||||||
|
@ -142,7 +142,7 @@ class SubscriptionPlanSwitch extends Component
|
|||||||
{
|
{
|
||||||
$this->hide_button = true;
|
$this->hide_button = true;
|
||||||
|
|
||||||
$response = $this->target->service()->createChangePlanCredit([
|
$response = $this->target->service()->createChangePlanCreditV2([
|
||||||
'recurring_invoice' => $this->recurring_invoice,
|
'recurring_invoice' => $this->recurring_invoice,
|
||||||
'subscription' => $this->subscription,
|
'subscription' => $this->subscription,
|
||||||
'target' => $this->target,
|
'target' => $this->target,
|
||||||
|
51
app/Http/Livewire/SubscriptionsTable.php
Normal file
51
app/Http/Livewire/SubscriptionsTable.php
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\RecurringInvoice;
|
||||||
|
use App\Utils\Traits\WithSorting;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Livewire\WithPagination;
|
||||||
|
|
||||||
|
class SubscriptionsTable extends Component
|
||||||
|
{
|
||||||
|
use WithPagination;
|
||||||
|
use WithSorting;
|
||||||
|
|
||||||
|
public $per_page = 10;
|
||||||
|
|
||||||
|
public $company;
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
MultiDB::setDb($this->company->db);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
$query = RecurringInvoice::query()
|
||||||
|
->where('client_id', auth()->guard('contact')->user()->client->id)
|
||||||
|
->where('company_id', $this->company->id)
|
||||||
|
->whereNotNull('subscription_id')
|
||||||
|
->where('is_deleted', false)
|
||||||
|
->where('status_id', RecurringInvoice::STATUS_ACTIVE)
|
||||||
|
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||||
|
->withTrashed()
|
||||||
|
->paginate($this->per_page);
|
||||||
|
|
||||||
|
return render('components.livewire.subscriptions-table', [
|
||||||
|
'recurring_invoices' => $query,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@ use Illuminate\Http\Request;
|
|||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use Laravel\Socialite\Facades\Socialite;
|
||||||
use stdClass;
|
use stdClass;
|
||||||
|
|
||||||
class PasswordProtection
|
class PasswordProtection
|
||||||
@ -111,7 +112,18 @@ class PasswordProtection
|
|||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
elseif(auth()->user()->oauth_provider_id == 'apple')
|
||||||
|
{
|
||||||
|
|
||||||
|
$user = Socialite::driver('apple')->userFromToken($request->header('X-API-OAUTH-PASSWORD'));
|
||||||
|
|
||||||
|
if($user && ($user->email == auth()->user()->email)){
|
||||||
|
|
||||||
|
Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout);
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return response()->json($error, 412);
|
return response()->json($error, 412);
|
||||||
|
@ -47,7 +47,7 @@ class RegisterRequest extends FormRequest
|
|||||||
|
|
||||||
foreach ($rules as $field => $properties) {
|
foreach ($rules as $field => $properties) {
|
||||||
if ($field === 'email') {
|
if ($field === 'email') {
|
||||||
$rules[$field] = array_merge($rules[$field], ['email:rfc,dns', 'max:255', Rule::unique('client_contacts')->where('company_id', $this->company()->id)]);
|
$rules[$field] = array_merge($rules[$field], ['email:rfc,dns', 'max:191', Rule::unique('client_contacts')->where('company_id', $this->company()->id)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($field === 'current_password') {
|
if ($field === 'current_password') {
|
||||||
|
@ -40,8 +40,6 @@ class UpdateCompanyRequest extends Request
|
|||||||
return auth()->user()->can('edit', $this->company);
|
return auth()->user()->can('edit', $this->company);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
$input = $this->all();
|
$input = $this->all();
|
||||||
@ -110,6 +108,7 @@ class UpdateCompanyRequest extends Request
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(isset($settings['email_style_custom']))
|
||||||
$settings['email_style_custom'] = str_replace(['{{','}}'], ['',''], $settings['email_style_custom']);
|
$settings['email_style_custom'] = str_replace(['{{','}}'], ['',''], $settings['email_style_custom']);
|
||||||
|
|
||||||
if (! $account->isFreeHostedClient()) {
|
if (! $account->isFreeHostedClient()) {
|
||||||
|
@ -41,7 +41,7 @@ class StoreExpenseRequest extends Request
|
|||||||
$rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id);
|
$rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! empty($this->client_id)) {
|
if ($this->client_id) {
|
||||||
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,10 @@ class UpdateExpenseRequest extends Request
|
|||||||
$rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id)->ignore($this->expense->id);
|
$rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id)->ignore($this->expense->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->client_id) {
|
||||||
|
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||||
|
}
|
||||||
|
|
||||||
$rules['category_id'] = 'bail|sometimes|nullable|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
$rules['category_id'] = 'bail|sometimes|nullable|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||||
|
|
||||||
return $this->globalRules($rules);
|
return $this->globalRules($rules);
|
||||||
|
@ -47,7 +47,8 @@ class PaymentWebhookRequest extends Request
|
|||||||
{
|
{
|
||||||
MultiDB::findAndSetDbByCompanyKey($this->company_key);
|
MultiDB::findAndSetDbByCompanyKey($this->company_key);
|
||||||
|
|
||||||
return CompanyGateway::withTrashed()->findOrFail($this->decodePrimaryKey($this->company_gateway_id));
|
return CompanyGateway::withTrashed()->find($this->decodePrimaryKey($this->company_gateway_id));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,6 +43,7 @@ class StoreRecurringExpenseRequest extends Request
|
|||||||
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$rules['category_id'] = 'bail|nullable|sometimes|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||||
$rules['frequency_id'] = 'required|integer|digits_between:1,12';
|
$rules['frequency_id'] = 'required|integer|digits_between:1,12';
|
||||||
$rules['tax_amount1'] = 'numeric';
|
$rules['tax_amount1'] = 'numeric';
|
||||||
$rules['tax_amount2'] = 'numeric';
|
$rules['tax_amount2'] = 'numeric';
|
||||||
@ -61,10 +62,6 @@ class StoreRecurringExpenseRequest extends Request
|
|||||||
$input['next_send_date_client'] = $input['next_send_date'];
|
$input['next_send_date_client'] = $input['next_send_date'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists('category_id', $input) && is_string($input['category_id'])) {
|
|
||||||
$input['category_id'] = $this->decodePrimaryKey($input['category_id']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! array_key_exists('currency_id', $input) || strlen($input['currency_id']) == 0) {
|
if (! array_key_exists('currency_id', $input) || strlen($input['currency_id']) == 0) {
|
||||||
$input['currency_id'] = (string) auth()->user()->company()->settings->currency_id;
|
$input['currency_id'] = (string) auth()->user()->company()->settings->currency_id;
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,7 @@ class UpdateRecurringExpenseRequest extends Request
|
|||||||
$rules['tax_amount1'] = 'numeric';
|
$rules['tax_amount1'] = 'numeric';
|
||||||
$rules['tax_amount2'] = 'numeric';
|
$rules['tax_amount2'] = 'numeric';
|
||||||
$rules['tax_amount3'] = 'numeric';
|
$rules['tax_amount3'] = 'numeric';
|
||||||
|
$rules['category_id'] = 'bail|nullable|sometimes|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||||
|
|
||||||
return $this->globalRules($rules);
|
return $this->globalRules($rules);
|
||||||
}
|
}
|
||||||
@ -70,10 +71,6 @@ class UpdateRecurringExpenseRequest extends Request
|
|||||||
$input['next_send_date_client'] = $input['next_send_date'];
|
$input['next_send_date_client'] = $input['next_send_date'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists('category_id', $input) && is_string($input['category_id'])) {
|
|
||||||
$input['category_id'] = $this->decodePrimaryKey($input['category_id']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists('documents', $input)) {
|
if (array_key_exists('documents', $input)) {
|
||||||
unset($input['documents']);
|
unset($input['documents']);
|
||||||
}
|
}
|
||||||
|
71
app/Http/Requests/Report/ProductSalesReportRequest.php
Normal file
71
app/Http/Requests/Report/ProductSalesReportRequest.php
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Requests\Report;
|
||||||
|
|
||||||
|
use App\Http\Requests\Request;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class ProductSalesReportRequest extends Request
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize() : bool
|
||||||
|
{
|
||||||
|
return auth()->user()->isAdmin();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
|
||||||
|
return [
|
||||||
|
'date_range' => 'bail|required|string',
|
||||||
|
'end_date' => 'bail|required_if:date_range,custom|nullable|date',
|
||||||
|
'start_date' => 'bail|required_if:date_range,custom|nullable|date',
|
||||||
|
'report_keys' => 'bail|present|array',
|
||||||
|
'send_email' => 'bail|required|bool',
|
||||||
|
'client_id' => 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id.',is_deleted,0',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function prepareForValidation()
|
||||||
|
{
|
||||||
|
$input = $this->all();
|
||||||
|
|
||||||
|
if (! array_key_exists('date_range', $input)) {
|
||||||
|
$input['date_range'] = 'all';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! array_key_exists('report_keys', $input)) {
|
||||||
|
$input['report_keys'] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! array_key_exists('send_email', $input)) {
|
||||||
|
$input['send_email'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('date_range', $input) && $input['date_range'] != 'custom') {
|
||||||
|
$input['start_date'] = null;
|
||||||
|
$input['end_date'] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(array_key_exists('client_id', $input) && strlen($input['client_id']) >=1)
|
||||||
|
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
|
||||||
|
|
||||||
|
$this->replace($input);
|
||||||
|
}
|
||||||
|
}
|
@ -29,6 +29,7 @@ class BlackListRule implements Rule
|
|||||||
'dataservices.space',
|
'dataservices.space',
|
||||||
'karenkey.com',
|
'karenkey.com',
|
||||||
'sharklasers.com',
|
'sharklasers.com',
|
||||||
|
'100072641.help'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,7 +20,7 @@ use App\Http\Requests\Quote\StoreQuoteRequest;
|
|||||||
use App\Import\ImportException;
|
use App\Import\ImportException;
|
||||||
use App\Jobs\Mail\NinjaMailerJob;
|
use App\Jobs\Mail\NinjaMailerJob;
|
||||||
use App\Jobs\Mail\NinjaMailerObject;
|
use App\Jobs\Mail\NinjaMailerObject;
|
||||||
use App\Mail\Import\ImportCompleted;
|
use App\Mail\Import\CsvImportCompleted;
|
||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Models\Quote;
|
use App\Models\Quote;
|
||||||
@ -187,6 +187,10 @@ class BaseImport
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$entity = $this->transformer->transform($record);
|
$entity = $this->transformer->transform($record);
|
||||||
|
|
||||||
|
if(!$entity)
|
||||||
|
continue;
|
||||||
|
|
||||||
$validator = $this->runValidation($entity);
|
$validator = $this->runValidation($entity);
|
||||||
|
|
||||||
if ($validator->fails()) {
|
if ($validator->fails()) {
|
||||||
@ -282,6 +286,8 @@ class BaseImport
|
|||||||
|
|
||||||
public function ingestInvoices($invoices, $invoice_number_key)
|
public function ingestInvoices($invoices, $invoice_number_key)
|
||||||
{
|
{
|
||||||
|
$count = 0;
|
||||||
|
|
||||||
$invoice_transformer = $this->transformer;
|
$invoice_transformer = $this->transformer;
|
||||||
|
|
||||||
/** @var PaymentRepository $payment_repository */
|
/** @var PaymentRepository $payment_repository */
|
||||||
@ -343,6 +349,7 @@ class BaseImport
|
|||||||
}
|
}
|
||||||
$invoice_repository->save($invoice_data, $invoice);
|
$invoice_repository->save($invoice_data, $invoice);
|
||||||
|
|
||||||
|
$count++;
|
||||||
// If we're doing a generic CSV import, only import payment data if we're not importing a payment CSV.
|
// If we're doing a generic CSV import, only import payment data if we're not importing a payment CSV.
|
||||||
// If we're doing a platform-specific import, trust the platform to only return payment info if there's not a separate payment CSV.
|
// If we're doing a platform-specific import, trust the platform to only return payment info if there's not a separate payment CSV.
|
||||||
if (
|
if (
|
||||||
@ -404,6 +411,9 @@ class BaseImport
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $count;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function actionInvoiceStatus(
|
private function actionInvoiceStatus(
|
||||||
@ -475,6 +485,8 @@ class BaseImport
|
|||||||
|
|
||||||
public function ingestQuotes($quotes, $quote_number_key)
|
public function ingestQuotes($quotes, $quote_number_key)
|
||||||
{
|
{
|
||||||
|
$count = 0;
|
||||||
|
|
||||||
$quote_transformer = $this->transformer;
|
$quote_transformer = $this->transformer;
|
||||||
|
|
||||||
/** @var ClientRepository $client_repository */
|
/** @var ClientRepository $client_repository */
|
||||||
@ -533,6 +545,8 @@ class BaseImport
|
|||||||
}
|
}
|
||||||
$quote_repository->save($quote_data, $quote);
|
$quote_repository->save($quote_data, $quote);
|
||||||
|
|
||||||
|
$count++;
|
||||||
|
|
||||||
$this->actionQuoteStatus(
|
$this->actionQuoteStatus(
|
||||||
$quote,
|
$quote,
|
||||||
$quote_data,
|
$quote_data,
|
||||||
@ -552,7 +566,11 @@ class BaseImport
|
|||||||
'error' => $message,
|
'error' => $message,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $count;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getUserIDForRecord($record)
|
protected function getUserIDForRecord($record)
|
||||||
@ -586,10 +604,11 @@ class BaseImport
|
|||||||
$data = [
|
$data = [
|
||||||
'errors' => $this->error_array,
|
'errors' => $this->error_array,
|
||||||
'company' => $this->company,
|
'company' => $this->company,
|
||||||
|
'entity_count' => $this->entity_count
|
||||||
];
|
];
|
||||||
|
|
||||||
$nmo = new NinjaMailerObject;
|
$nmo = new NinjaMailerObject;
|
||||||
$nmo->mailable = new ImportCompleted($this->company, $data);
|
$nmo->mailable = new CsvImportCompleted($this->company, $data);
|
||||||
$nmo->company = $this->company;
|
$nmo->company = $this->company;
|
||||||
$nmo->settings = $this->company->settings;
|
$nmo->settings = $this->company->settings;
|
||||||
$nmo->to_user = $this->company->owner();
|
$nmo->to_user = $this->company->owner();
|
||||||
|
@ -37,7 +37,7 @@ use App\Import\Transformer\Csv\PaymentTransformer;
|
|||||||
use App\Import\Transformer\Csv\ProductTransformer;
|
use App\Import\Transformer\Csv\ProductTransformer;
|
||||||
use App\Import\Transformer\Csv\QuoteTransformer;
|
use App\Import\Transformer\Csv\QuoteTransformer;
|
||||||
use App\Import\Transformer\Csv\VendorTransformer;
|
use App\Import\Transformer\Csv\VendorTransformer;
|
||||||
use App\Import\Transformers\Bank\BankTransformer;
|
use App\Import\Transformer\Bank\BankTransformer;
|
||||||
use App\Repositories\BankTransactionRepository;
|
use App\Repositories\BankTransactionRepository;
|
||||||
use App\Repositories\ClientRepository;
|
use App\Repositories\ClientRepository;
|
||||||
use App\Repositories\ExpenseRepository;
|
use App\Repositories\ExpenseRepository;
|
||||||
|
@ -66,7 +66,6 @@ class Wave extends BaseImport implements ImportInterface
|
|||||||
|
|
||||||
if (empty($data)) {
|
if (empty($data)) {
|
||||||
$this->entity_count['clients'] = 0;
|
$this->entity_count['clients'] = 0;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,11 +169,16 @@ class Wave extends BaseImport implements ImportInterface
|
|||||||
$entity_type = 'expense';
|
$entity_type = 'expense';
|
||||||
|
|
||||||
$data = $this->getCsvData($entity_type);
|
$data = $this->getCsvData($entity_type);
|
||||||
|
|
||||||
|
if(!$data){
|
||||||
|
$this->entity_count['expense'] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$data = $this->preTransform($data, $entity_type);
|
$data = $this->preTransform($data, $entity_type);
|
||||||
|
|
||||||
if (empty($data)) {
|
if (empty($data)) {
|
||||||
$this->entity_count['expense'] = 0;
|
$this->entity_count['expense'] = 0;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,6 +216,8 @@ class Wave extends BaseImport implements ImportInterface
|
|||||||
|
|
||||||
public function ingestExpenses($data)
|
public function ingestExpenses($data)
|
||||||
{
|
{
|
||||||
|
$count = 0;
|
||||||
|
|
||||||
$key = 'Transaction ID';
|
$key = 'Transaction ID';
|
||||||
|
|
||||||
$expense_transformer = $this->transformer;
|
$expense_transformer = $this->transformer;
|
||||||
@ -255,6 +261,7 @@ class Wave extends BaseImport implements ImportInterface
|
|||||||
);
|
);
|
||||||
|
|
||||||
$expense_repository->save($expense_data, $expense);
|
$expense_repository->save($expense_data, $expense);
|
||||||
|
$count++;
|
||||||
}
|
}
|
||||||
} catch (\Exception $ex) {
|
} catch (\Exception $ex) {
|
||||||
if ($ex instanceof ImportException) {
|
if ($ex instanceof ImportException) {
|
||||||
@ -270,5 +277,8 @@ class Wave extends BaseImport implements ImportInterface
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $count;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
* @license https://www.elastic.co/licensing/elastic-license
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace App\Import\Transformers\Bank;
|
namespace App\Import\Transformer\Bank;
|
||||||
|
|
||||||
use App\Import\ImportException;
|
use App\Import\ImportException;
|
||||||
use App\Import\Transformer\BaseTransformer;
|
use App\Import\Transformer\BaseTransformer;
|
@ -61,9 +61,9 @@ class InvoiceTransformer extends BaseTransformer
|
|||||||
'discount' => $this->getFreshbookQuantityFloat($record, 'Discount Percentage'),
|
'discount' => $this->getFreshbookQuantityFloat($record, 'Discount Percentage'),
|
||||||
'is_amount_discount' => false,
|
'is_amount_discount' => false,
|
||||||
'tax_name1' => $this->getString($record, 'Tax 1 Type'),
|
'tax_name1' => $this->getString($record, 'Tax 1 Type'),
|
||||||
'tax_rate1' => $this->getFreshbookQuantityFloat($record, 'Tax 1 Amount'),
|
'tax_rate1' => $this->calcTaxRate($record, 'Tax 1 Amount'),
|
||||||
'tax_name2' => $this->getString($record, 'Tax 2 Type'),
|
'tax_name2' => $this->getString($record, 'Tax 2 Type'),
|
||||||
'tax_rate2' => $this->getFreshbookQuantityFloat($record, 'Tax 2 Amount'),
|
'tax_rate2' => $this->calcTaxRate($record, 'Tax 2 Amount'),
|
||||||
];
|
];
|
||||||
$transformed['amount'] += $this->getFreshbookQuantityFloat($record, 'Line Total');
|
$transformed['amount'] += $this->getFreshbookQuantityFloat($record, 'Line Total');
|
||||||
}
|
}
|
||||||
@ -79,6 +79,27 @@ class InvoiceTransformer extends BaseTransformer
|
|||||||
return $transformed;
|
return $transformed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Line Subtotal
|
||||||
|
public function calcTaxRate($record, $field)
|
||||||
|
{
|
||||||
|
if(isset($record['Line Subtotal']) && $record['Line Subtotal'] > 0)
|
||||||
|
return ($record[$field] / $record['Line Subtotal']) * 100;
|
||||||
|
|
||||||
|
$tax_amount1 = isset($record['Tax 1 Amount']) ? $record['Tax 1 Amount'] : 0;
|
||||||
|
|
||||||
|
$tax_amount2 = isset($record['Tax 2 Amount']) ? $record['Tax 2 Amount'] : 0;
|
||||||
|
|
||||||
|
$line_total = isset($record['Line Total']) ? $record['Line Total'] : 0;
|
||||||
|
|
||||||
|
$subtotal = $line_total - $tax_amount2 - $tax_amount1;
|
||||||
|
|
||||||
|
if($subtotal > 0)
|
||||||
|
return $record[$field] / $subtotal * 100;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/** @return float */
|
/** @return float */
|
||||||
public function getFreshbookQuantityFloat($data, $field)
|
public function getFreshbookQuantityFloat($data, $field)
|
||||||
{
|
{
|
||||||
|
@ -28,6 +28,11 @@ class InvoiceTransformer extends BaseTransformer
|
|||||||
*/
|
*/
|
||||||
public function transform($invoice_data)
|
public function transform($invoice_data)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (!isset($invoice_data['DocumentNumber'])) {
|
||||||
|
throw new ImportException('DocumentNumber key not found in this import file.');
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->hasInvoice($invoice_data['DocumentNumber'])) {
|
if ($this->hasInvoice($invoice_data['DocumentNumber'])) {
|
||||||
throw new ImportException('Invoice number already exists');
|
throw new ImportException('Invoice number already exists');
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,8 @@ class ClientTransformer extends BaseTransformer
|
|||||||
public function transform($data)
|
public function transform($data)
|
||||||
{
|
{
|
||||||
if (isset($data['customer_name']) && $this->hasClient($data['customer_name'])) {
|
if (isset($data['customer_name']) && $this->hasClient($data['customer_name'])) {
|
||||||
throw new ImportException('Client already exists');
|
return false;
|
||||||
|
// throw new ImportException('Client already exists');
|
||||||
}
|
}
|
||||||
|
|
||||||
$settings = new \stdClass;
|
$settings = new \stdClass;
|
||||||
|
@ -1,430 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Invoice Ninja (https://invoiceninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Import\Transformers;
|
|
||||||
|
|
||||||
use App\Models\ClientContact;
|
|
||||||
use App\Utils\Number;
|
|
||||||
use Carbon;
|
|
||||||
use Exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class BaseTransformer.
|
|
||||||
*/
|
|
||||||
class BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var
|
|
||||||
*/
|
|
||||||
protected $maps;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* BaseTransformer constructor.
|
|
||||||
*
|
|
||||||
* @param $maps
|
|
||||||
*/
|
|
||||||
public function __construct($maps)
|
|
||||||
{
|
|
||||||
$this->maps = $maps;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $data
|
|
||||||
* @param $field
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getString($data, $field)
|
|
||||||
{
|
|
||||||
return (isset($data[$field]) && $data[$field]) ? $data[$field] : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getInvoiceTypeId($data, $field)
|
|
||||||
{
|
|
||||||
return (isset($data[$field]) && $data[$field]) ? $data[$field] : '1';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCurrencyByCode($data, $key = 'client.currency_id')
|
|
||||||
{
|
|
||||||
$code = array_key_exists($key, $data) ? $data[$key] : false;
|
|
||||||
|
|
||||||
return $this->maps['currencies'][$code] ?? $this->maps['company']->settings->currency_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getClient($client_name, $client_email)
|
|
||||||
{
|
|
||||||
$clients = $this->maps['company']->clients;
|
|
||||||
|
|
||||||
$client_id_search = $clients->where('id_number', $client_name);
|
|
||||||
|
|
||||||
if ($client_id_search->count() >= 1) {
|
|
||||||
return $client_id_search->first()->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
$client_name_search = $clients->where('name', $client_name);
|
|
||||||
|
|
||||||
if ($client_name_search->count() >= 1) {
|
|
||||||
return $client_name_search->first()->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! empty($client_email)) {
|
|
||||||
$contacts = ClientContact::where('company_id', $this->maps['company']->id)
|
|
||||||
->where('email', $client_email);
|
|
||||||
|
|
||||||
if ($contacts->count() >= 1) {
|
|
||||||
return $contacts->first()->client_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/**
|
|
||||||
* @param $name
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function hasClient($name)
|
|
||||||
{
|
|
||||||
$name = trim(strtolower($name));
|
|
||||||
|
|
||||||
return isset($this->maps['client'][$name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $name
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function hasVendor($name)
|
|
||||||
{
|
|
||||||
$name = trim(strtolower($name));
|
|
||||||
|
|
||||||
return isset($this->maps['vendor'][$name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $key
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function hasProduct($key)
|
|
||||||
{
|
|
||||||
$key = trim(strtolower($key));
|
|
||||||
|
|
||||||
return isset($this->maps['product'][$key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $data
|
|
||||||
* @param $field
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getNumber($data, $field)
|
|
||||||
{
|
|
||||||
return (isset($data->$field) && $data->$field) ? $data->$field : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $data
|
|
||||||
* @param $field
|
|
||||||
*
|
|
||||||
* @return float
|
|
||||||
*/
|
|
||||||
public function getFloat($data, $field)
|
|
||||||
{
|
|
||||||
if (array_key_exists($field, $data)) {
|
|
||||||
$number = preg_replace('/[^0-9-.]+/', '', $data[$field]);
|
|
||||||
} else {
|
|
||||||
$number = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Number::parseFloat($number);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFloatWithSamePrecision($data, $field)
|
|
||||||
{
|
|
||||||
$precision = (int) strpos(strrev($data[$field]), ".");
|
|
||||||
|
|
||||||
return round($data[$field], $precision);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param $name
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function getClientId($name)
|
|
||||||
{
|
|
||||||
$name = strtolower(trim($name));
|
|
||||||
|
|
||||||
return isset($this->maps['client'][$name]) ? $this->maps['client'][$name] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $name
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function getProduct($data, $key, $field, $default = false)
|
|
||||||
{
|
|
||||||
$productKey = trim(strtolower($data->$key));
|
|
||||||
|
|
||||||
if (! isset($this->maps['product'][$productKey])) {
|
|
||||||
return $default;
|
|
||||||
}
|
|
||||||
|
|
||||||
$product = $this->maps['product'][$productKey];
|
|
||||||
|
|
||||||
return $product->$field ?: $default;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $name
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function getContact($email)
|
|
||||||
{
|
|
||||||
$email = trim(strtolower($email));
|
|
||||||
|
|
||||||
if (! isset($this->maps['contact'][$email])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->maps['contact'][$email];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $name
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function getCustomer($key)
|
|
||||||
{
|
|
||||||
$key = trim($key);
|
|
||||||
|
|
||||||
if (! isset($this->maps['customer'][$key])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->maps['customer'][$key];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $name
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function getCountryId($name)
|
|
||||||
{
|
|
||||||
$name = strtolower(trim($name));
|
|
||||||
|
|
||||||
if (strlen($name) == 2) {
|
|
||||||
return $this->getCountryIdBy2($name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return isset($this->maps['countries'][$name]) ? $this->maps['countries'][$name] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $name
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function getCountryIdBy2($name)
|
|
||||||
{
|
|
||||||
$name = strtolower(trim($name));
|
|
||||||
|
|
||||||
return isset($this->maps['countries2'][$name]) ? $this->maps['countries2'][$name] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $name
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function getTaxRate($name)
|
|
||||||
{
|
|
||||||
$name = strtolower(trim($name));
|
|
||||||
|
|
||||||
return isset($this->maps['tax_rates'][$name]) ? $this->maps['tax_rates'][$name] : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $name
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function getTaxName($name)
|
|
||||||
{
|
|
||||||
$name = strtolower(trim($name));
|
|
||||||
|
|
||||||
return isset($this->maps['tax_names'][$name]) ? $this->maps['tax_names'][$name] : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $name
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getFirstName($name)
|
|
||||||
{
|
|
||||||
$name = Utils::splitName($name);
|
|
||||||
|
|
||||||
return $name[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $date
|
|
||||||
* @param string $format
|
|
||||||
* @param mixed $data
|
|
||||||
* @param mixed $field
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function getDate($data, $field)
|
|
||||||
{
|
|
||||||
if ($date = data_get($data, $field)) {
|
|
||||||
try {
|
|
||||||
$date = new Carbon($date);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
// if we fail to parse return blank
|
|
||||||
$date = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $date ? $date->format('Y-m-d') : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $name
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getLastName($name)
|
|
||||||
{
|
|
||||||
$name = Utils::splitName($name);
|
|
||||||
|
|
||||||
return $name[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $number
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getInvoiceNumber($number)
|
|
||||||
{
|
|
||||||
return $number ? ltrim(trim($number), '0') : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $invoiceNumber
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function getInvoiceId($invoiceNumber)
|
|
||||||
{
|
|
||||||
$invoiceNumber = $this->getInvoiceNumber($invoiceNumber);
|
|
||||||
$invoiceNumber = strtolower($invoiceNumber);
|
|
||||||
|
|
||||||
return isset($this->maps['invoice'][$invoiceNumber]) ? $this->maps['invoice'][$invoiceNumber] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $invoiceNumber
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function getInvoicePublicId($invoiceNumber)
|
|
||||||
{
|
|
||||||
$invoiceNumber = $this->getInvoiceNumber($invoiceNumber);
|
|
||||||
$invoiceNumber = strtolower($invoiceNumber);
|
|
||||||
|
|
||||||
return isset($this->maps['invoice'][$invoiceNumber]) ? $this->maps['invoices'][$invoiceNumber]->public_id : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $invoiceNumber
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function hasInvoice($invoiceNumber)
|
|
||||||
{
|
|
||||||
$invoiceNumber = $this->getInvoiceNumber($invoiceNumber);
|
|
||||||
$invoiceNumber = strtolower($invoiceNumber);
|
|
||||||
|
|
||||||
return $this->maps['invoice'][$invoiceNumber] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $invoiceNumber
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function getInvoiceClientId($invoiceNumber)
|
|
||||||
{
|
|
||||||
$invoiceNumber = $this->getInvoiceNumber($invoiceNumber);
|
|
||||||
$invoiceNumber = strtolower($invoiceNumber);
|
|
||||||
|
|
||||||
return $this->maps['invoice_client'][$invoiceNumber] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $name
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function getVendorId($name)
|
|
||||||
{
|
|
||||||
$name = strtolower(trim($name));
|
|
||||||
|
|
||||||
return $this->maps['vendor'][$name] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $name
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function getExpenseCategoryId($name)
|
|
||||||
{
|
|
||||||
$name = strtolower(trim($name));
|
|
||||||
|
|
||||||
return $this->maps['expense_category'][$name] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $name
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function getProjectId($name)
|
|
||||||
{
|
|
||||||
$name = strtolower(trim($name));
|
|
||||||
|
|
||||||
return $this->maps['project'][$name] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $name
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function getPaymentTypeId($name)
|
|
||||||
{
|
|
||||||
$name = strtolower(trim($name));
|
|
||||||
|
|
||||||
return $this->maps['payment_type'][$name] ?? null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Invoice Ninja (https://invoiceninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Import\Transformers;
|
|
||||||
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class ClientTransformer.
|
|
||||||
*/
|
|
||||||
class ClientTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param $data
|
|
||||||
*
|
|
||||||
* @return bool|Item
|
|
||||||
*/
|
|
||||||
public function transform($data)
|
|
||||||
{
|
|
||||||
if (isset($data->name) && $this->hasClient($data->name)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$settings = new \stdClass;
|
|
||||||
$settings->currency_id = (string) $this->getCurrencyByCode($data);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'company_id' => $this->maps['company']->id,
|
|
||||||
'name' => $this->getString($data, 'client.name'),
|
|
||||||
'phone' => $this->getString($data, 'client.phone'),
|
|
||||||
'address1' => $this->getString($data, 'client.address1'),
|
|
||||||
'address2' => $this->getString($data, 'client.address2'),
|
|
||||||
'city' => $this->getString($data, 'client.city'),
|
|
||||||
'state' => $this->getString($data, 'client.state'),
|
|
||||||
'shipping_address1' => $this->getString($data, 'client.shipping_address1'),
|
|
||||||
'shipping_address2' => $this->getString($data, 'client.shipping_address2'),
|
|
||||||
'shipping_city' => $this->getString($data, 'client.shipping_city'),
|
|
||||||
'shipping_state' => $this->getString($data, 'client.shipping_state'),
|
|
||||||
'shipping_postal_code' => $this->getString($data, 'client.shipping_postal_code'),
|
|
||||||
'public_notes' => $this->getString($data, 'client.public_notes'),
|
|
||||||
'private_notes' => $this->getString($data, 'client.private_notes'),
|
|
||||||
'website' => $this->getString($data, 'client.website'),
|
|
||||||
'vat_number' => $this->getString($data, 'client.vat_number'),
|
|
||||||
'id_number' => $this->getString($data, 'client.id_number'),
|
|
||||||
'custom_value1' => $this->getString($data, 'client.custom1'),
|
|
||||||
'custom_value2' => $this->getString($data, 'client.custom2'),
|
|
||||||
'custom_value3' => $this->getString($data, 'client.custom3'),
|
|
||||||
'custom_value4' => $this->getString($data, 'client.custom4'),
|
|
||||||
'balance' => $this->getFloat($data, 'client.balance'),
|
|
||||||
'paid_to_date' => $this->getFloat($data, 'client.paid_to_date'),
|
|
||||||
'credit_balance' => 0,
|
|
||||||
'settings' => $settings,
|
|
||||||
'client_hash' => Str::random(40),
|
|
||||||
'contacts' => [
|
|
||||||
[
|
|
||||||
'first_name' => $this->getString($data, 'contact.first_name'),
|
|
||||||
'last_name' => $this->getString($data, 'contact.last_name'),
|
|
||||||
'email' => $this->getString($data, 'contact.email'),
|
|
||||||
'phone' => $this->getString($data, 'contact.phone'),
|
|
||||||
'custom_value1' => $this->getString($data, 'contact.custom1'),
|
|
||||||
'custom_value2' => $this->getString($data, 'contact.custom2'),
|
|
||||||
'custom_value3' => $this->getString($data, 'contact.custom3'),
|
|
||||||
'custom_value4' => $this->getString($data, 'contact.custom4'),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'country_id' => isset($data->country_id) ? $this->getCountryId($data->country_id) : null,
|
|
||||||
'shipping_country_id' => isset($data->shipping_country_id) ? $this->getCountryId($data->shipping_country_id) : null,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,120 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* client Ninja (https://clientninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/clientninja/clientninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. client Ninja LLC (https://clientninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Import\Transformers\Csv;
|
|
||||||
|
|
||||||
use App\Import\ImportException;
|
|
||||||
use App\Import\Transformers\BaseTransformer;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class ClientTransformer.
|
|
||||||
*/
|
|
||||||
class ClientTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param $data
|
|
||||||
*
|
|
||||||
* @return array|bool
|
|
||||||
*/
|
|
||||||
public function transform($data)
|
|
||||||
{
|
|
||||||
if (isset($data->name) && $this->hasClient($data->name)) {
|
|
||||||
throw new ImportException('Client already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
$settings = new \stdClass();
|
|
||||||
$settings->currency_id = (string) $this->getCurrencyByCode($data);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'company_id' => $this->maps['company']->id,
|
|
||||||
'name' => $this->getString($data, 'client.name'),
|
|
||||||
'phone' => $this->getString($data, 'client.phone'),
|
|
||||||
'address1' => $this->getString($data, 'client.address1'),
|
|
||||||
'address2' => $this->getString($data, 'client.address2'),
|
|
||||||
'postal_code' => $this->getString($data, 'client.postal_code'),
|
|
||||||
'city' => $this->getString($data, 'client.city'),
|
|
||||||
'state' => $this->getString($data, 'client.state'),
|
|
||||||
'shipping_address1' => $this->getString(
|
|
||||||
$data,
|
|
||||||
'client.shipping_address1'
|
|
||||||
),
|
|
||||||
'shipping_address2' => $this->getString(
|
|
||||||
$data,
|
|
||||||
'client.shipping_address2'
|
|
||||||
),
|
|
||||||
'shipping_city' => $this->getString($data, 'client.shipping_city'),
|
|
||||||
'shipping_state' => $this->getString(
|
|
||||||
$data,
|
|
||||||
'client.shipping_state'
|
|
||||||
),
|
|
||||||
'shipping_postal_code' => $this->getString(
|
|
||||||
$data,
|
|
||||||
'client.shipping_postal_code'
|
|
||||||
),
|
|
||||||
'public_notes' => $this->getString($data, 'client.public_notes'),
|
|
||||||
'private_notes' => $this->getString($data, 'client.private_notes'),
|
|
||||||
'website' => $this->getString($data, 'client.website'),
|
|
||||||
'vat_number' => $this->getString($data, 'client.vat_number'),
|
|
||||||
'id_number' => $this->getString($data, 'client.id_number'),
|
|
||||||
'custom_value1' => $this->getString($data, 'client.custom_value1'),
|
|
||||||
'custom_value2' => $this->getString($data, 'client.custom_value2'),
|
|
||||||
'custom_value3' => $this->getString($data, 'client.custom_value3'),
|
|
||||||
'custom_value4' => $this->getString($data, 'client.custom_value4'),
|
|
||||||
'balance' => preg_replace(
|
|
||||||
'/[^0-9,.]+/',
|
|
||||||
'',
|
|
||||||
$this->getFloat($data, 'client.balance')
|
|
||||||
),
|
|
||||||
'paid_to_date' => preg_replace(
|
|
||||||
'/[^0-9,.]+/',
|
|
||||||
'',
|
|
||||||
$this->getFloat($data, 'client.paid_to_date')
|
|
||||||
),
|
|
||||||
'credit_balance' => 0,
|
|
||||||
'settings' => $settings,
|
|
||||||
'client_hash' => Str::random(40),
|
|
||||||
'contacts' => [
|
|
||||||
[
|
|
||||||
'first_name' => $this->getString(
|
|
||||||
$data,
|
|
||||||
'contact.first_name'
|
|
||||||
),
|
|
||||||
'last_name' => $this->getString($data, 'contact.last_name'),
|
|
||||||
'email' => $this->getString($data, 'contact.email'),
|
|
||||||
'phone' => $this->getString($data, 'contact.phone'),
|
|
||||||
'custom_value1' => $this->getString(
|
|
||||||
$data,
|
|
||||||
'contact.custom_value1'
|
|
||||||
),
|
|
||||||
'custom_value2' => $this->getString(
|
|
||||||
$data,
|
|
||||||
'contact.custom_value2'
|
|
||||||
),
|
|
||||||
'custom_value3' => $this->getString(
|
|
||||||
$data,
|
|
||||||
'contact.custom_value3'
|
|
||||||
),
|
|
||||||
'custom_value4' => $this->getString(
|
|
||||||
$data,
|
|
||||||
'contact.custom_value4'
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'country_id' => isset($data['client.country'])
|
|
||||||
? $this->getCountryId($data['client.country'])
|
|
||||||
: null,
|
|
||||||
'shipping_country_id' => isset($data['client.shipping_country'])
|
|
||||||
? $this->getCountryId($data['client.shipping_country'])
|
|
||||||
: null,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Import\Transformers\Csv;
|
|
||||||
|
|
||||||
use App\Import\Transformers\BaseTransformer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class InvoiceTransformer.
|
|
||||||
*/
|
|
||||||
class ExpenseTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param $data
|
|
||||||
*
|
|
||||||
* @return bool|array
|
|
||||||
*/
|
|
||||||
public function transform($data)
|
|
||||||
{
|
|
||||||
$clientId = isset($data['expense.client']) ? $this->getClientId($data['expense.client']) : null;
|
|
||||||
|
|
||||||
return [
|
|
||||||
'company_id' => $this->maps['company']->id,
|
|
||||||
'amount' => $this->getFloat($data, 'expense.amount'),
|
|
||||||
'currency_id' => $this->getCurrencyByCode($data, 'expense.currency_id'),
|
|
||||||
'vendor_id' => isset($data['expense.vendor']) ? $this->getVendorId($data['expense.vendor']) : null,
|
|
||||||
'client_id' => isset($data['expense.client']) ? $this->getClientId($data['expense.client']) : null,
|
|
||||||
'date' => isset($data['expense.date']) ? date('Y-m-d', strtotime($data['expense.date'])) : null,
|
|
||||||
'public_notes' => $this->getString($data, 'expense.public_notes'),
|
|
||||||
'private_notes' => $this->getString($data, 'expense.private_notes'),
|
|
||||||
'category_id' => isset($data['expense.category']) ? $this->getExpenseCategoryId($data['expense.category']) : null,
|
|
||||||
'project_id' => isset($data['expense.project']) ? $this->getProjectId($data['expense.project']) : null,
|
|
||||||
'payment_type_id' => isset($data['expense.payment_type']) ? $this->getPaymentTypeId($data['expense.payment_type']) : null,
|
|
||||||
'payment_date' => isset($data['expense.payment_date']) ? date('Y-m-d', strtotime($data['expense.payment_date'])) : null,
|
|
||||||
'custom_value1' => $this->getString($data, 'expense.custom_value1'),
|
|
||||||
'custom_value2' => $this->getString($data, 'expense.custom_value2'),
|
|
||||||
'custom_value3' => $this->getString($data, 'expense.custom_value3'),
|
|
||||||
'custom_value4' => $this->getString($data, 'expense.custom_value4'),
|
|
||||||
'transaction_reference' => $this->getString($data, 'expense.transaction_reference'),
|
|
||||||
'should_be_invoiced' => $clientId ? true : false,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,133 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* client Ninja (https://clientninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/clientninja/clientninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. client Ninja LLC (https://clientninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Import\Transformers\Csv;
|
|
||||||
|
|
||||||
use App\Import\ImportException;
|
|
||||||
use App\Import\Transformers\BaseTransformer;
|
|
||||||
use App\Models\Invoice;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class InvoiceTransformer.
|
|
||||||
*/
|
|
||||||
class InvoiceTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param $data
|
|
||||||
*
|
|
||||||
* @return bool|array
|
|
||||||
*/
|
|
||||||
public function transform($line_items_data)
|
|
||||||
{
|
|
||||||
$invoice_data = reset($line_items_data);
|
|
||||||
|
|
||||||
if ($this->hasInvoice($invoice_data['invoice.number'])) {
|
|
||||||
throw new ImportException('Invoice number already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
$invoiceStatusMap = [
|
|
||||||
'sent' => Invoice::STATUS_SENT,
|
|
||||||
'draft' => Invoice::STATUS_DRAFT,
|
|
||||||
];
|
|
||||||
|
|
||||||
$transformed = [
|
|
||||||
'company_id' => $this->maps['company']->id,
|
|
||||||
'number' => $this->getString($invoice_data, 'invoice.number'),
|
|
||||||
'user_id' => $this->getString($invoice_data, 'invoice.user_id'),
|
|
||||||
'amount' => $amount = $this->getFloat($invoice_data, 'invoice.amount'),
|
|
||||||
'balance' => isset($invoice_data['invoice.balance']) ? $this->getFloat($invoice_data, 'invoice.balance') : $amount,
|
|
||||||
'client_id' => $this->getClient($this->getString($invoice_data, 'client.name'), $this->getString($invoice_data, 'client.email')),
|
|
||||||
'discount' => $this->getFloat($invoice_data, 'invoice.discount'),
|
|
||||||
'po_number' => $this->getString($invoice_data, 'invoice.po_number'),
|
|
||||||
'date' => isset($invoice_data['invoice.date']) ? date('Y-m-d', strtotime($invoice_data['invoice.date'])) : now()->format('Y-m-d'),
|
|
||||||
'due_date' => isset($invoice_data['invoice.due_date']) ? date('Y-m-d', strtotime($invoice_data['invoice.due_date'])) : null,
|
|
||||||
'terms' => $this->getString($invoice_data, 'invoice.terms'),
|
|
||||||
'public_notes' => $this->getString($invoice_data, 'invoice.public_notes'),
|
|
||||||
// 'is_sent' => $this->getString( $invoice_data, 'invoice.is_sent' ),
|
|
||||||
'private_notes' => $this->getString($invoice_data, 'invoice.private_notes'),
|
|
||||||
'tax_name1' => $this->getString($invoice_data, 'invoice.tax_name1'),
|
|
||||||
'tax_rate1' => $this->getFloat($invoice_data, 'invoice.tax_rate1'),
|
|
||||||
'tax_name2' => $this->getString($invoice_data, 'invoice.tax_name2'),
|
|
||||||
'tax_rate2' => $this->getFloat($invoice_data, 'invoice.tax_rate2'),
|
|
||||||
'tax_name3' => $this->getString($invoice_data, 'invoice.tax_name3'),
|
|
||||||
'tax_rate3' => $this->getFloat($invoice_data, 'invoice.tax_rate3'),
|
|
||||||
'custom_value1' => $this->getString($invoice_data, 'invoice.custom_value1'),
|
|
||||||
'custom_value2' => $this->getString($invoice_data, 'invoice.custom_value2'),
|
|
||||||
'custom_value3' => $this->getString($invoice_data, 'invoice.custom_value3'),
|
|
||||||
'custom_value4' => $this->getString($invoice_data, 'invoice.custom_value4'),
|
|
||||||
'footer' => $this->getString($invoice_data, 'invoice.footer'),
|
|
||||||
'partial' => $this->getFloat($invoice_data, 'invoice.partial'),
|
|
||||||
'partial_due_date' => $this->getString($invoice_data, 'invoice.partial_due_date'),
|
|
||||||
'custom_surcharge1' => $this->getString($invoice_data, 'invoice.custom_surcharge1'),
|
|
||||||
'custom_surcharge2' => $this->getString($invoice_data, 'invoice.custom_surcharge2'),
|
|
||||||
'custom_surcharge3' => $this->getString($invoice_data, 'invoice.custom_surcharge3'),
|
|
||||||
'custom_surcharge4' => $this->getString($invoice_data, 'invoice.custom_surcharge4'),
|
|
||||||
'exchange_rate' => $this->getString($invoice_data, 'invoice.exchange_rate'),
|
|
||||||
'status_id' => $invoiceStatusMap[$status =
|
|
||||||
strtolower($this->getString($invoice_data, 'invoice.status'))] ??
|
|
||||||
Invoice::STATUS_SENT,
|
|
||||||
// 'viewed' => $status === 'viewed',
|
|
||||||
'archived' => $status === 'archived',
|
|
||||||
];
|
|
||||||
|
|
||||||
if (isset($invoice_data['payment.amount'])) {
|
|
||||||
$transformed['payments'] = [
|
|
||||||
[
|
|
||||||
'date' => isset($invoice_data['payment.date']) ? date('Y-m-d', strtotime($invoice_data['payment.date'])) : date('y-m-d'),
|
|
||||||
'transaction_reference' => $this->getString($invoice_data, 'payment.transaction_reference'),
|
|
||||||
'amount' => $this->getFloat($invoice_data, 'payment.amount'),
|
|
||||||
],
|
|
||||||
];
|
|
||||||
} elseif ($status === 'paid') {
|
|
||||||
$transformed['payments'] = [
|
|
||||||
[
|
|
||||||
'date' => isset($invoice_data['payment.date']) ? date('Y-m-d', strtotime($invoice_data['payment.date'])) : date('y-m-d'),
|
|
||||||
'transaction_reference' => $this->getString($invoice_data, 'payment.transaction_reference'),
|
|
||||||
'amount' => $this->getFloat($invoice_data, 'invoice.amount'),
|
|
||||||
],
|
|
||||||
];
|
|
||||||
} elseif (isset($transformed['amount']) && isset($transformed['balance']) && ($transformed['amount'] != $transformed['balance'])) {
|
|
||||||
$transformed['payments'] = [
|
|
||||||
[
|
|
||||||
'date' => isset($invoice_data['payment.date']) ? date('Y-m-d', strtotime($invoice_data['payment.date'])) : date('y-m-d'),
|
|
||||||
'transaction_reference' => $this->getString($invoice_data, 'payment.transaction_reference'),
|
|
||||||
'amount' => $transformed['amount'] - $transformed['balance'],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$line_items = [];
|
|
||||||
foreach ($line_items_data as $record) {
|
|
||||||
$line_items[] = [
|
|
||||||
'quantity' => $this->getFloat($record, 'item.quantity'),
|
|
||||||
'cost' => $this->getFloat($record, 'item.cost'),
|
|
||||||
'product_key' => $this->getString($record, 'item.product_key'),
|
|
||||||
'notes' => $this->getString($record, 'item.notes'),
|
|
||||||
'discount' => $this->getFloat($record, 'item.discount'),
|
|
||||||
'is_amount_discount' => filter_var($this->getString($record, 'item.is_amount_discount'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE),
|
|
||||||
'tax_name1' => $this->getString($record, 'item.tax_name1'),
|
|
||||||
'tax_rate1' => $this->getFloat($record, 'item.tax_rate1'),
|
|
||||||
'tax_name2' => $this->getString($record, 'item.tax_name2'),
|
|
||||||
'tax_rate2' => $this->getFloat($record, 'item.tax_rate2'),
|
|
||||||
'tax_name3' => $this->getString($record, 'item.tax_name3'),
|
|
||||||
'tax_rate3' => $this->getFloat($record, 'item.tax_rate3'),
|
|
||||||
'custom_value1' => $this->getString($record, 'item.custom_value1'),
|
|
||||||
'custom_value2' => $this->getString($record, 'item.custom_value2'),
|
|
||||||
'custom_value3' => $this->getString($record, 'item.custom_value3'),
|
|
||||||
'custom_value4' => $this->getString($record, 'item.custom_value4'),
|
|
||||||
'type_id' => $this->getInvoiceTypeId($record, 'item.type_id'),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
$transformed['line_items'] = $line_items;
|
|
||||||
|
|
||||||
return $transformed;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* client Ninja (https://clientninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/clientninja/clientninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. client Ninja LLC (https://clientninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Import\Transformers\Csv;
|
|
||||||
|
|
||||||
use App\Import\ImportException;
|
|
||||||
use App\Import\Transformers\BaseTransformer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class PaymentTransformer.
|
|
||||||
*/
|
|
||||||
class PaymentTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param $data
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function transform($data)
|
|
||||||
{
|
|
||||||
$client_id =
|
|
||||||
$this->getClient($this->getString($data, 'payment.client_id'), $this->getString($data, 'payment.client_id'));
|
|
||||||
|
|
||||||
if (empty($client_id)) {
|
|
||||||
throw new ImportException('Could not find client.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$transformed = [
|
|
||||||
'company_id' => $this->maps['company']->id,
|
|
||||||
'number' => $this->getString($data, 'payment.number'),
|
|
||||||
'user_id' => $this->getString($data, 'payment.user_id'),
|
|
||||||
'amount' => $this->getFloat($data, 'payment.amount'),
|
|
||||||
'refunded' => $this->getFloat($data, 'payment.refunded'),
|
|
||||||
'applied' => $this->getFloat($data, 'payment.applied'),
|
|
||||||
'transaction_reference' => $this->getString($data, 'payment.transaction_reference '),
|
|
||||||
'date' => $this->getString($data, 'payment.date'),
|
|
||||||
'private_notes' => $this->getString($data, 'payment.private_notes'),
|
|
||||||
'custom_value1' => $this->getString($data, 'payment.custom_value1'),
|
|
||||||
'custom_value2' => $this->getString($data, 'payment.custom_value2'),
|
|
||||||
'custom_value3' => $this->getString($data, 'payment.custom_value3'),
|
|
||||||
'custom_value4' => $this->getString($data, 'payment.custom_value4'),
|
|
||||||
'client_id' => $client_id,
|
|
||||||
];
|
|
||||||
|
|
||||||
if (isset($data['payment.invoice_number']) &&
|
|
||||||
$invoice_id = $this->getInvoiceId($data['payment.invoice_number'])) {
|
|
||||||
$transformed['invoices'] = [
|
|
||||||
[
|
|
||||||
'invoice_id' => $invoice_id,
|
|
||||||
'amount' => $transformed['amount'] ?? null,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $transformed;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Invoice Ninja (https://invoiceninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Import\Transformers\Csv;
|
|
||||||
|
|
||||||
use App\Import\Transformers\BaseTransformer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class ProductTransformer.
|
|
||||||
*/
|
|
||||||
class ProductTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param $data
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function transform($data)
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'company_id' => $this->maps['company']->id,
|
|
||||||
'product_key' => $this->getString($data, 'product.product_key'),
|
|
||||||
'notes' => $this->getString($data, 'product.notes'),
|
|
||||||
'cost' => $this->getFloat($data, 'product.cost'),
|
|
||||||
'price' => $this->getFloat($data, 'product.price'),
|
|
||||||
'quantity' => $this->getFloat($data, 'product.quantity'),
|
|
||||||
'tax_name1' => $this->getString($data, 'product.tax_name1'),
|
|
||||||
'tax_rate1' => $this->getFloat($data, 'product.tax_rate1'),
|
|
||||||
'tax_name2' => $this->getString($data, 'product.tax_name2'),
|
|
||||||
'tax_rate2' => $this->getFloat($data, 'product.tax_rate2'),
|
|
||||||
'tax_name3' => $this->getString($data, 'product.tax_name3'),
|
|
||||||
'tax_rate3' => $this->getFloat($data, 'product.tax_rate3'),
|
|
||||||
'custom_value1' => $this->getString($data, 'product.custom_value1'),
|
|
||||||
'custom_value2' => $this->getString($data, 'product.custom_value2'),
|
|
||||||
'custom_value3' => $this->getString($data, 'product.custom_value3'),
|
|
||||||
'custom_value4' => $this->getString($data, 'product.custom_value4'),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Import\Transformers\Csv;
|
|
||||||
|
|
||||||
use App\Import\ImportException;
|
|
||||||
use App\Import\Transformers\BaseTransformer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class VendorTransformer.
|
|
||||||
*/
|
|
||||||
class VendorTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param $data
|
|
||||||
*
|
|
||||||
* @return array|bool
|
|
||||||
*/
|
|
||||||
public function transform($data)
|
|
||||||
{
|
|
||||||
if (isset($data->name) && $this->hasVendor($data->name)) {
|
|
||||||
throw new ImportException('Vendor already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'company_id' => $this->maps['company']->id,
|
|
||||||
'name' => $this->getString($data, 'vendor.name'),
|
|
||||||
'phone' => $this->getString($data, 'vendor.phone'),
|
|
||||||
'id_number' => $this->getString($data, 'vendor.id_number'),
|
|
||||||
'vat_number' => $this->getString($data, 'vendor.vat_number'),
|
|
||||||
'website' => $this->getString($data, 'vendor.website'),
|
|
||||||
'currency_id' => $this->getCurrencyByCode($data, 'vendor.currency_id'),
|
|
||||||
'public_notes' => $this->getString($data, 'vendor.public_notes'),
|
|
||||||
'private_notes' => $this->getString($data, 'vendor.private_notes'),
|
|
||||||
'address1' => $this->getString($data, 'vendor.address1'),
|
|
||||||
'address2' => $this->getString($data, 'vendor.address2'),
|
|
||||||
'city' => $this->getString($data, 'vendor.city'),
|
|
||||||
'state' => $this->getString($data, 'vendor.state'),
|
|
||||||
'postal_code' => $this->getString($data, 'vendor.postal_code'),
|
|
||||||
'custom_value1' => $this->getString($data, 'vendor.custom_value1'),
|
|
||||||
'custom_value2' => $this->getString($data, 'vendor.custom_value2'),
|
|
||||||
'custom_value3' => $this->getString($data, 'vendor.custom_value3'),
|
|
||||||
'custom_value4' => $this->getString($data, 'vendor.custom_value4'),
|
|
||||||
'vendor_contacts' => [
|
|
||||||
[
|
|
||||||
'first_name' => $this->getString($data, 'vendor.first_name'),
|
|
||||||
'last_name' => $this->getString($data, 'vendor.last_name'),
|
|
||||||
'email' => $this->getString($data, 'vendor.email'),
|
|
||||||
'phone' => $this->getString($data, 'vendor.phone'),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'country_id' => isset($data['vendor.country_id']) ? $this->getCountryId($data['vendor.country_id']) : null,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Invoice Ninja (https://clientninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/clientninja/clientninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. client Ninja LLC (https://clientninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Import\Transformers\Freshbooks;
|
|
||||||
|
|
||||||
use App\Import\ImportException;
|
|
||||||
use App\Import\Transformers\BaseTransformer;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class ClientTransformer.
|
|
||||||
*/
|
|
||||||
class ClientTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param $data
|
|
||||||
*
|
|
||||||
* @return array|bool
|
|
||||||
*/
|
|
||||||
public function transform($data)
|
|
||||||
{
|
|
||||||
if (isset($data['Organization']) && $this->hasClient($data['Organization'])) {
|
|
||||||
throw new ImportException('Client already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'company_id' => $this->maps['company']->id,
|
|
||||||
'name' => $this->getString($data, 'Organization'),
|
|
||||||
'phone' => $this->getString($data, 'Phone'),
|
|
||||||
'address1' => $this->getString($data, 'Street'),
|
|
||||||
'city' => $this->getString($data, 'City'),
|
|
||||||
'state' => $this->getString($data, 'Province/State'),
|
|
||||||
'postal_code' => $this->getString($data, 'Postal Code'),
|
|
||||||
'country_id' => isset($data['Country']) ? $this->getCountryId($data['Country']) : null,
|
|
||||||
'private_notes' => $this->getString($data, 'Notes'),
|
|
||||||
'credit_balance' => 0,
|
|
||||||
'settings' => new \stdClass,
|
|
||||||
'client_hash' => Str::random(40),
|
|
||||||
'contacts' => [
|
|
||||||
[
|
|
||||||
'first_name' => $this->getString($data, 'First Name'),
|
|
||||||
'last_name' => $this->getString($data, 'Last Name'),
|
|
||||||
'email' => $this->getString($data, 'Email'),
|
|
||||||
'phone' => $this->getString($data, 'Phone'),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* client Ninja (https://clientninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/clientninja/clientninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. client Ninja LLC (https://clientninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Import\Transformers\Freshbooks;
|
|
||||||
|
|
||||||
use App\Import\ImportException;
|
|
||||||
use App\Import\Transformers\BaseTransformer;
|
|
||||||
use App\Models\Invoice;
|
|
||||||
use App\Utils\Number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class InvoiceTransformer.
|
|
||||||
*/
|
|
||||||
class InvoiceTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param $line_items_data
|
|
||||||
*
|
|
||||||
* @return bool|array
|
|
||||||
*/
|
|
||||||
public function transform($line_items_data)
|
|
||||||
{
|
|
||||||
$invoice_data = reset($line_items_data);
|
|
||||||
|
|
||||||
if ($this->hasInvoice($invoice_data['Invoice #'])) {
|
|
||||||
throw new ImportException('Invoice number already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
$invoiceStatusMap = [
|
|
||||||
'sent' => Invoice::STATUS_SENT,
|
|
||||||
'draft' => Invoice::STATUS_DRAFT,
|
|
||||||
];
|
|
||||||
|
|
||||||
$transformed = [
|
|
||||||
'company_id' => $this->maps['company']->id,
|
|
||||||
'client_id' => $this->getClient($this->getString($invoice_data, 'Client Name'), null),
|
|
||||||
'number' => $this->getString($invoice_data, 'Invoice #'),
|
|
||||||
'date' => isset($invoice_data['Date Issued']) ? date('Y-m-d', strtotime($invoice_data['Date Issued'])) : null,
|
|
||||||
'currency_id' => $this->getCurrencyByCode($invoice_data, 'Currency'),
|
|
||||||
'amount' => 0,
|
|
||||||
'status_id' => $invoiceStatusMap[$status =
|
|
||||||
strtolower($this->getString($invoice_data, 'Invoice Status'))] ?? Invoice::STATUS_SENT,
|
|
||||||
// 'viewed' => $status === 'viewed',
|
|
||||||
];
|
|
||||||
|
|
||||||
$line_items = [];
|
|
||||||
foreach ($line_items_data as $record) {
|
|
||||||
$line_items[] = [
|
|
||||||
'product_key' => $this->getString($record, 'Item Name'),
|
|
||||||
'notes' => $this->getString($record, 'Item Description'),
|
|
||||||
'cost' => $this->getFreshbookQuantityFloat($record, 'Rate'),
|
|
||||||
'quantity' => $this->getFreshbookQuantityFloat($record, 'Quantity'),
|
|
||||||
'discount' => $this->getFreshbookQuantityFloat($record, 'Discount Percentage'),
|
|
||||||
'is_amount_discount' => false,
|
|
||||||
'tax_name1' => $this->getString($record, 'Tax 1 Type'),
|
|
||||||
'tax_rate1' => $this->getFreshbookQuantityFloat($record, 'Tax 1 Amount'),
|
|
||||||
'tax_name2' => $this->getString($record, 'Tax 2 Type'),
|
|
||||||
'tax_rate2' => $this->getFreshbookQuantityFloat($record, 'Tax 2 Amount'),
|
|
||||||
];
|
|
||||||
$transformed['amount'] += $this->getFreshbookQuantityFloat($record, 'Line Total');
|
|
||||||
}
|
|
||||||
$transformed['line_items'] = $line_items;
|
|
||||||
|
|
||||||
if (! empty($invoice_data['Date Paid'])) {
|
|
||||||
$transformed['payments'] = [[
|
|
||||||
'date' => date('Y-m-d', strtotime($invoice_data['Date Paid'])),
|
|
||||||
'amount' => $transformed['amount'],
|
|
||||||
]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $transformed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return float */
|
|
||||||
public function getFreshbookQuantityFloat($data, $field)
|
|
||||||
{
|
|
||||||
return $data[$field];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* client Ninja (https://clientninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/clientninja/clientninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. client Ninja LLC (https://clientninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Import\Transformers\Invoice2Go;
|
|
||||||
|
|
||||||
use App\Import\ImportException;
|
|
||||||
use App\Import\Transformers\BaseTransformer;
|
|
||||||
use App\Models\Invoice;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class InvoiceTransformer.
|
|
||||||
*/
|
|
||||||
class InvoiceTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param $line_items_data
|
|
||||||
*
|
|
||||||
* @return bool|array
|
|
||||||
*/
|
|
||||||
public function transform($invoice_data)
|
|
||||||
{
|
|
||||||
if ($this->hasInvoice($invoice_data['DocumentNumber'])) {
|
|
||||||
throw new ImportException('Invoice number already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
$invoiceStatusMap = [
|
|
||||||
'unsent' => Invoice::STATUS_DRAFT,
|
|
||||||
'sent' => Invoice::STATUS_SENT,
|
|
||||||
];
|
|
||||||
|
|
||||||
$transformed = [
|
|
||||||
'company_id' => $this->maps['company']->id,
|
|
||||||
'number' => $this->getString($invoice_data, 'DocumentNumber'),
|
|
||||||
'notes' => $this->getString($invoice_data, 'Comment'),
|
|
||||||
'date' => isset($invoice_data['DocumentDate']) ? date('Y-m-d', strtotime($invoice_data['DocumentDate'])) : null,
|
|
||||||
'currency_id' => $this->getCurrencyByCode($invoice_data, 'Currency'),
|
|
||||||
'amount' => 0,
|
|
||||||
'status_id' => $invoiceStatusMap[$status =
|
|
||||||
strtolower($this->getString($invoice_data, 'DocumentStatus'))] ?? Invoice::STATUS_SENT,
|
|
||||||
// 'viewed' => $status === 'viewed',
|
|
||||||
'line_items' => [
|
|
||||||
[
|
|
||||||
'amount' => $amount = $this->getFloat($invoice_data, 'TotalAmount'),
|
|
||||||
'quantity' => 1,
|
|
||||||
'discount' => $this->getFloat($invoice_data, 'DiscountValue'),
|
|
||||||
'is_amount_discount' => false,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
$client_id =
|
|
||||||
$this->getClient($this->getString($invoice_data, 'Name'), $this->getString($invoice_data, 'EmailRecipient'));
|
|
||||||
|
|
||||||
if ($client_id) {
|
|
||||||
$transformed['client_id'] = $client_id;
|
|
||||||
} else {
|
|
||||||
$transformed['client'] = [
|
|
||||||
'name' => $this->getString($invoice_data, 'Name'),
|
|
||||||
'address1' => $this->getString($invoice_data, 'DocumentRecipientAddress'),
|
|
||||||
'shipping_address1' => $this->getString($invoice_data, 'ShipAddress'),
|
|
||||||
'credit_balance' => 0,
|
|
||||||
'settings' => new \stdClass,
|
|
||||||
'client_hash' => Str::random(40),
|
|
||||||
'contacts' => [
|
|
||||||
[
|
|
||||||
'email' => $this->getString($invoice_data, 'Email'),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
if (! empty($invoice_data['Date Paid'])) {
|
|
||||||
$transformed['payments'] = [
|
|
||||||
[
|
|
||||||
'date' => date('Y-m-d', strtotime($invoice_data['DatePaid'])),
|
|
||||||
'amount' => $this->getFloat($invoice_data, 'Payments'),
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $transformed;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Invoice Ninja (https://invoiceninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Import\Transformers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class InvoiceItemTransformer.
|
|
||||||
*/
|
|
||||||
class InvoiceItemTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param $data
|
|
||||||
*
|
|
||||||
* @return bool|Item
|
|
||||||
*/
|
|
||||||
public function transform($data)
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'quantity' => $this->getFloat($data, 'item.quantity'),
|
|
||||||
'cost' => $this->getFloat($data, 'item.cost'),
|
|
||||||
'product_key' => $this->getString($data, 'item.product_key'),
|
|
||||||
'notes' => $this->getString($data, 'item.notes'),
|
|
||||||
'discount' => $this->getFloat($data, 'item.discount'),
|
|
||||||
'is_amount_discount' => $this->getString($data, 'item.is_amount_discount'),
|
|
||||||
'tax_name1' => $this->getString($data, 'item.tax_name1'),
|
|
||||||
'tax_rate1' => $this->getFloat($data, 'item.tax_rate1'),
|
|
||||||
'tax_name2' => $this->getString($data, 'item.tax_name2'),
|
|
||||||
'tax_rate2' => $this->getFloat($data, 'item.tax_rate2'),
|
|
||||||
'tax_name3' => $this->getString($data, 'item.tax_name3'),
|
|
||||||
'tax_rate3' => $this->getFloat($data, 'item.tax_rate3'),
|
|
||||||
'custom_value1' => $this->getString($data, 'item.custom_value1'),
|
|
||||||
'custom_value2' => $this->getString($data, 'item.custom_value2'),
|
|
||||||
'custom_value3' => $this->getString($data, 'item.custom_value3'),
|
|
||||||
'custom_value4' => $this->getString($data, 'item.custom_value4'),
|
|
||||||
'type_id' => $this->getInvoiceTypeId($data, 'item.type_id'),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Invoice Ninja (https://invoiceninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Import\Transformers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class InvoiceTransformer.
|
|
||||||
*/
|
|
||||||
class InvoiceTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param $data
|
|
||||||
*
|
|
||||||
* @return bool|Item
|
|
||||||
*/
|
|
||||||
public function transform($data)
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'company_id' => $this->maps['company']->id,
|
|
||||||
'number' => $this->getString($data, 'invoice.number'),
|
|
||||||
'user_id' => $this->getString($data, 'invoice.user_id'),
|
|
||||||
'amount' => $this->getFloat($data, 'invoice.amount'),
|
|
||||||
'balance' => $this->getFloat($data, 'invoice.balance'),
|
|
||||||
'client_id' => $this->getClient($this->getString($data, 'client.name'), $this->getString($data, 'client.email')),
|
|
||||||
'discount' => $this->getFloat($data, 'invoice.discount'),
|
|
||||||
'po_number' => $this->getString($data, 'invoice.po_number'),
|
|
||||||
'date' => $this->getString($data, 'invoice.date'),
|
|
||||||
'due_date' => $this->getString($data, 'invoice.due_date'),
|
|
||||||
'terms' => $this->getString($data, 'invoice.terms'),
|
|
||||||
'public_notes' => $this->getString($data, 'invoice.public_notes'),
|
|
||||||
'is_sent' => $this->getString($data, 'invoice.is_sent'),
|
|
||||||
'private_notes' => $this->getString($data, 'invoice.private_notes'),
|
|
||||||
'tax_name1' => $this->getString($data, 'invoice.tax_name1'),
|
|
||||||
'tax_rate1' => $this->getFloatWithSamePrecision($data, 'invoice.tax_rate1'),
|
|
||||||
'tax_name2' => $this->getString($data, 'invoice.tax_name2'),
|
|
||||||
'tax_rate2' => $this->getFloatWithSamePrecision($data, 'invoice.tax_rate2'),
|
|
||||||
'tax_name3' => $this->getString($data, 'invoice.tax_name3'),
|
|
||||||
'tax_rate3' => $this->getFloatWithSamePrecision($data, 'invoice.tax_rate3'),
|
|
||||||
'custom_value1' => $this->getString($data, 'invoice.custom_value1'),
|
|
||||||
'custom_value2' => $this->getString($data, 'invoice.custom_value2'),
|
|
||||||
'custom_value3' => $this->getString($data, 'invoice.custom_value3'),
|
|
||||||
'custom_value4' => $this->getString($data, 'invoice.custom_value4'),
|
|
||||||
'footer' => $this->getString($data, 'invoice.footer'),
|
|
||||||
'partial' => $this->getFloat($data, 'invoice.partial'),
|
|
||||||
'partial_due_date' => $this->getString($data, 'invoice.partial_due_date'),
|
|
||||||
'custom_surcharge1' => $this->getString($data, 'invoice.custom_surcharge1'),
|
|
||||||
'custom_surcharge2' => $this->getString($data, 'invoice.custom_surcharge2'),
|
|
||||||
'custom_surcharge3' => $this->getString($data, 'invoice.custom_surcharge3'),
|
|
||||||
'custom_surcharge4' => $this->getString($data, 'invoice.custom_surcharge4'),
|
|
||||||
'exchange_rate' => $this->getString($data, 'invoice.exchange_rate'),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Invoice Ninja (https://clientninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/clientninja/clientninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. client Ninja LLC (https://clientninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Import\Transformers\Invoicely;
|
|
||||||
|
|
||||||
use App\Import\ImportException;
|
|
||||||
use App\Import\Transformers\BaseTransformer;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class ClientTransformer.
|
|
||||||
*/
|
|
||||||
class ClientTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param $data
|
|
||||||
*
|
|
||||||
* @return array|bool
|
|
||||||
*/
|
|
||||||
public function transform($data)
|
|
||||||
{
|
|
||||||
if (isset($data['Client Name']) && $this->hasClient($data['Client Name'])) {
|
|
||||||
throw new ImportException('Client already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'company_id' => $this->maps['company']->id,
|
|
||||||
'name' => $this->getString($data, 'Client Name'),
|
|
||||||
'phone' => $this->getString($data, 'Phone'),
|
|
||||||
'country_id' => isset($data['Country']) ? $this->getCountryIdBy2($data['Country']) : null,
|
|
||||||
'credit_balance' => 0,
|
|
||||||
'settings' => new \stdClass,
|
|
||||||
'client_hash' => Str::random(40),
|
|
||||||
'contacts' => [
|
|
||||||
[
|
|
||||||
'email' => $this->getString($data, 'Email'),
|
|
||||||
'phone' => $this->getString($data, 'Phone'),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* client Ninja (https://clientninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/clientninja/clientninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. client Ninja LLC (https://clientninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Import\Transformers\Invoicely;
|
|
||||||
|
|
||||||
use App\Import\ImportException;
|
|
||||||
use App\Import\Transformers\BaseTransformer;
|
|
||||||
use App\Models\Invoice;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class InvoiceTransformer.
|
|
||||||
*/
|
|
||||||
class InvoiceTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param $data
|
|
||||||
*
|
|
||||||
* @return bool|array
|
|
||||||
*/
|
|
||||||
public function transform($data)
|
|
||||||
{
|
|
||||||
if ($this->hasInvoice($data['Details'])) {
|
|
||||||
throw new ImportException('Invoice number already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
$transformed = [
|
|
||||||
'company_id' => $this->maps['company']->id,
|
|
||||||
'client_id' => $this->getClient($this->getString($data, 'Client'), null),
|
|
||||||
'number' => $this->getString($data, 'Details'),
|
|
||||||
'date' => isset($data['Date']) ? date('Y-m-d', strtotime($data['Date'])) : null,
|
|
||||||
'due_date' => isset($data['Due']) ? date('Y-m-d', strtotime($data['Due'])) : null,
|
|
||||||
'status_id' => Invoice::STATUS_SENT,
|
|
||||||
'line_items' => [
|
|
||||||
[
|
|
||||||
'cost' => $amount = $this->getFloat($data, 'Total'),
|
|
||||||
'quantity' => 1,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
if (strtolower($data['Status']) === 'paid') {
|
|
||||||
$transformed['payments'] = [
|
|
||||||
[
|
|
||||||
'date' => date('Y-m-d'),
|
|
||||||
'amount' => $amount,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $transformed;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Invoice Ninja (https://invoiceninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Import\Transformers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class PaymentTransformer.
|
|
||||||
*/
|
|
||||||
class PaymentTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param $data
|
|
||||||
*
|
|
||||||
* @return bool|Item
|
|
||||||
*/
|
|
||||||
public function transform($data)
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'company_id' => $this->maps['company']->id,
|
|
||||||
'number' => $this->getString($data, 'payment.number'),
|
|
||||||
'user_id' => $this->getString($data, 'payment.user_id'),
|
|
||||||
'amount' => $this->getFloat($data, 'payment.amount'),
|
|
||||||
'refunded' => $this->getFloat($data, 'payment.refunded'),
|
|
||||||
'applied' => $this->getFloat($data, 'payment.applied'),
|
|
||||||
'transaction_reference' => $this->getString($data, 'payment.transaction_reference '),
|
|
||||||
'date' => $this->getString($data, 'payment.date'),
|
|
||||||
'private_notes' => $this->getString($data, 'payment.private_notes'),
|
|
||||||
'number' => $this->getString($data, 'number'),
|
|
||||||
'custom_value1' => $this->getString($data, 'custom_value1'),
|
|
||||||
'custom_value2' => $this->getString($data, 'custom_value2'),
|
|
||||||
'custom_value3' => $this->getString($data, 'custom_value3'),
|
|
||||||
'custom_value4' => $this->getString($data, 'custom_value4'),
|
|
||||||
'client_id' => $this->getString($data, 'client_id'),
|
|
||||||
'invoice_number' => $this->getString($data, 'payment.invoice_number'),
|
|
||||||
'method' => $this,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Invoice Ninja (https://clientninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/clientninja/clientninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. client Ninja LLC (https://clientninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Import\Transformers\Waveaccounting;
|
|
||||||
|
|
||||||
use App\Import\ImportException;
|
|
||||||
use App\Import\Transformers\BaseTransformer;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class ClientTransformer.
|
|
||||||
*/
|
|
||||||
class ClientTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param $data
|
|
||||||
*
|
|
||||||
* @return array|bool
|
|
||||||
*/
|
|
||||||
public function transform($data)
|
|
||||||
{
|
|
||||||
if (isset($data['customer_name']) && $this->hasClient($data['customer_name'])) {
|
|
||||||
throw new ImportException('Client already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
$settings = new \stdClass;
|
|
||||||
$settings->currency_id = (string) $this->getCurrencyByCode($data, 'customer_currency');
|
|
||||||
|
|
||||||
if (strval($data['Payment Terms'] ?? '') > 0) {
|
|
||||||
$settings->payment_terms = $data['Payment Terms'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'company_id' => $this->maps['company']->id,
|
|
||||||
'name' => $this->getString($data, 'customer_name'),
|
|
||||||
'number' => $this->getString($data, 'account_number'),
|
|
||||||
'phone' => $this->getString($data, 'phone'),
|
|
||||||
'website' => $this->getString($data, 'website'),
|
|
||||||
'country_id' => ! empty($data['country']) ? $this->getCountryId($data['country']) : null,
|
|
||||||
'state' => $this->getString($data, 'province/state'),
|
|
||||||
'address1' => $this->getString($data, 'address_line_1'),
|
|
||||||
'address2' => $this->getString($data, 'address_line_2'),
|
|
||||||
'city' => $this->getString($data, 'city'),
|
|
||||||
'postal_code' => $this->getString($data, 'postal_code/zip_code'),
|
|
||||||
|
|
||||||
'shipping_country_id' => ! empty($data['ship-to_country']) ? $this->getCountryId($data['country']) : null,
|
|
||||||
'shipping_state' => $this->getString($data, 'ship-to_province/state'),
|
|
||||||
'shipping_address1' => $this->getString($data, 'ship-to_address_line_1'),
|
|
||||||
'shipping_address2' => $this->getString($data, 'ship-to_address_line_2'),
|
|
||||||
'shipping_city' => $this->getString($data, 'ship-to_city'),
|
|
||||||
'shipping_postal_code' => $this->getString($data, 'ship-to_postal_code/zip_code'),
|
|
||||||
'public_notes' => $this->getString($data, 'delivery_instructions'),
|
|
||||||
|
|
||||||
'credit_balance' => 0,
|
|
||||||
'settings' =>$settings,
|
|
||||||
'client_hash' => Str::random(40),
|
|
||||||
'contacts' => [
|
|
||||||
[
|
|
||||||
'first_name' => $this->getString($data, 'contact_first_name'),
|
|
||||||
'last_name' => $this->getString($data, 'contact_last_name'),
|
|
||||||
'email' => $this->getString($data, 'email'),
|
|
||||||
'phone' => $this->getString($data, 'phone'),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,83 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* client Ninja (https://clientninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/clientninja/clientninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. client Ninja LLC (https://clientninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Import\Transformers\Waveaccounting;
|
|
||||||
|
|
||||||
use App\Import\ImportException;
|
|
||||||
use App\Import\Transformers\BaseTransformer;
|
|
||||||
use App\Models\Invoice;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class InvoiceTransformer.
|
|
||||||
*/
|
|
||||||
class InvoiceTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param $line_items_data
|
|
||||||
*
|
|
||||||
* @return bool|array
|
|
||||||
*/
|
|
||||||
public function transform($line_items_data)
|
|
||||||
{
|
|
||||||
$invoice_data = reset($line_items_data);
|
|
||||||
|
|
||||||
if ($this->hasInvoice($invoice_data['Invoice Number'])) {
|
|
||||||
throw new ImportException('Invoice number already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
$transformed = [
|
|
||||||
'company_id' => $this->maps['company']->id,
|
|
||||||
'client_id' => $this->getClient($customer_name = $this->getString($invoice_data, 'Customer'), null),
|
|
||||||
'number' => $invoice_number = $this->getString($invoice_data, 'Invoice Number'),
|
|
||||||
'date' => date('Y-m-d', strtotime($invoice_data['Transaction Date'])) ?: now()->format('Y-m-d'), //27-01-2022
|
|
||||||
// 'date' => isset( $invoice_data['Invoice Date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['Transaction Date'] ) ) : null,
|
|
||||||
'currency_id' => $this->getCurrencyByCode($invoice_data, 'Currency'),
|
|
||||||
'status_id' => Invoice::STATUS_SENT,
|
|
||||||
];
|
|
||||||
|
|
||||||
$line_items = [];
|
|
||||||
$payments = [];
|
|
||||||
foreach ($line_items_data as $record) {
|
|
||||||
if ($record['Account Type'] === 'Income') {
|
|
||||||
$description = $this->getString($record, 'Transaction Line Description');
|
|
||||||
|
|
||||||
// Remove duplicate data from description
|
|
||||||
if (substr($description, 0, strlen($customer_name) + 3) === $customer_name.' - ') {
|
|
||||||
$description = substr($description, strlen($customer_name) + 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (substr($description, 0, strlen($invoice_number) + 3) === $invoice_number.' - ') {
|
|
||||||
$description = substr($description, strlen($invoice_number) + 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
$line_items[] = [
|
|
||||||
'notes' => $description,
|
|
||||||
'cost' => $this->getFloat($record, 'Amount Before Sales Tax'),
|
|
||||||
'tax_name1' => $this->getString($record, 'Sales Tax Name'),
|
|
||||||
'tax_rate1' => $this->getFloat($record, 'Sales Tax Amount'),
|
|
||||||
|
|
||||||
'quantity' => 1,
|
|
||||||
];
|
|
||||||
} elseif ($record['Account Type'] === 'System Receivable Invoice') {
|
|
||||||
// This is a payment
|
|
||||||
$payments[] = [
|
|
||||||
'date' => date('Y-m-d', strtotime($invoice_data['Transaction Date'])),
|
|
||||||
'amount' => $this->getFloat($record, 'Amount (One column)'),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$transformed['line_items'] = $line_items;
|
|
||||||
$transformed['payments'] = $payments;
|
|
||||||
|
|
||||||
return $transformed;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Invoice Ninja (https://clientninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/clientninja/clientninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. client Ninja LLC (https://clientninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Import\Transformers\Zoho;
|
|
||||||
|
|
||||||
use App\Import\ImportException;
|
|
||||||
use App\Import\Transformers\BaseTransformer;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class ClientTransformer.
|
|
||||||
*/
|
|
||||||
class ClientTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param $data
|
|
||||||
*
|
|
||||||
* @return array|bool
|
|
||||||
*/
|
|
||||||
public function transform($data)
|
|
||||||
{
|
|
||||||
if (isset($data['Company Name']) && $this->hasClient($data['Company Name'])) {
|
|
||||||
throw new ImportException('Client already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
$settings = new \stdClass;
|
|
||||||
$settings->currency_id = (string) $this->getCurrencyByCode($data, 'Currency');
|
|
||||||
|
|
||||||
if (strval($data['Payment Terms'] ?? '') > 0) {
|
|
||||||
$settings->payment_terms = $data['Payment Terms'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'company_id' => $this->maps['company']->id,
|
|
||||||
'name' => $this->getString($data, 'Company Name'),
|
|
||||||
'phone' => $this->getString($data, 'Phone'),
|
|
||||||
'private_notes' => $this->getString($data, 'Notes'),
|
|
||||||
'website' => $this->getString($data, 'Website'),
|
|
||||||
'id_number' => $this->getString($data, 'Customer ID'),
|
|
||||||
'address1' => $this->getString($data, 'Billing Address'),
|
|
||||||
'address2' => $this->getString($data, 'Billing Street2'),
|
|
||||||
'city' => $this->getString($data, 'Billing City'),
|
|
||||||
'state' => $this->getString($data, 'Billing State'),
|
|
||||||
'postal_code' => $this->getString($data, 'Billing Code'),
|
|
||||||
'country_id' => isset($data['Billing Country']) ? $this->getCountryId($data['Billing Country']) : null,
|
|
||||||
|
|
||||||
'shipping_address1' => $this->getString($data, 'Shipping Address'),
|
|
||||||
'shipping_address2' => $this->getString($data, 'Shipping Street2'),
|
|
||||||
'shipping_city' => $this->getString($data, 'Shipping City'),
|
|
||||||
'shipping_state' => $this->getString($data, 'Shipping State'),
|
|
||||||
'shipping_postal_code' => $this->getString($data, 'Shipping Code'),
|
|
||||||
'shipping_country_id' => isset($data['Shipping Country']) ? $this->getCountryId($data['Shipping Country']) : null,
|
|
||||||
'credit_balance' => 0,
|
|
||||||
'settings' => $settings,
|
|
||||||
'client_hash' => Str::random(40),
|
|
||||||
'contacts' => [
|
|
||||||
[
|
|
||||||
'first_name' => $this->getString($data, 'First Name'),
|
|
||||||
'last_name' => $this->getString($data, 'Last Name'),
|
|
||||||
'email' => $this->getString($data, 'Email'),
|
|
||||||
'phone' => $this->getString($data, 'Phone'),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* client Ninja (https://clientninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/clientninja/clientninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. client Ninja LLC (https://clientninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Import\Transformers\Zoho;
|
|
||||||
|
|
||||||
use App\Import\ImportException;
|
|
||||||
use App\Import\Transformers\BaseTransformer;
|
|
||||||
use App\Models\Invoice;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class InvoiceTransformer.
|
|
||||||
*/
|
|
||||||
class InvoiceTransformer extends BaseTransformer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param $line_items_data
|
|
||||||
*
|
|
||||||
* @return bool|array
|
|
||||||
*/
|
|
||||||
public function transform($line_items_data)
|
|
||||||
{
|
|
||||||
$invoice_data = reset($line_items_data);
|
|
||||||
|
|
||||||
if ($this->hasInvoice($invoice_data['Invoice Number'])) {
|
|
||||||
throw new ImportException('Invoice number already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
$invoiceStatusMap = [
|
|
||||||
'sent' => Invoice::STATUS_SENT,
|
|
||||||
'draft' => Invoice::STATUS_DRAFT,
|
|
||||||
];
|
|
||||||
|
|
||||||
$transformed = [
|
|
||||||
'company_id' => $this->maps['company']->id,
|
|
||||||
'client_id' => $this->getClient($this->getString($invoice_data, 'Customer ID'), null),
|
|
||||||
'number' => $this->getString($invoice_data, 'Invoice Number'),
|
|
||||||
'date' => isset($invoice_data['Invoice Date']) ? date('Y-m-d', strtotime($invoice_data['Invoice Date'])) : null,
|
|
||||||
'due_date' => isset($invoice_data['Due Date']) ? date('Y-m-d', strtotime($invoice_data['Due Date'])) : null,
|
|
||||||
'po_number' => $this->getString($invoice_data, 'PurchaseOrder'),
|
|
||||||
'public_notes' => $this->getString($invoice_data, 'Notes'),
|
|
||||||
// 'currency_id' => $this->getCurrencyByCode($invoice_data, 'Currency'),
|
|
||||||
'amount' => $this->getFloat($invoice_data, 'Total'),
|
|
||||||
'balance' => $this->getFloat($invoice_data, 'Balance'),
|
|
||||||
'status_id' => $invoiceStatusMap[$status =
|
|
||||||
strtolower($this->getString($invoice_data, 'Invoice Status'))] ?? Invoice::STATUS_SENT,
|
|
||||||
// 'viewed' => $status === 'viewed',
|
|
||||||
];
|
|
||||||
|
|
||||||
$line_items = [];
|
|
||||||
foreach ($line_items_data as $record) {
|
|
||||||
$line_items[] = [
|
|
||||||
'product_key' => $this->getString($record, 'Item Name'),
|
|
||||||
'notes' => $this->getString($record, 'Item Description'),
|
|
||||||
'cost' => round($this->getFloat($record, 'Item Price'), 2),
|
|
||||||
'quantity' => $this->getFloat($record, 'Quantity'),
|
|
||||||
'discount' => $this->getString($record, 'Discount Amount'),
|
|
||||||
'is_amount_discount' => true,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
$transformed['line_items'] = $line_items;
|
|
||||||
|
|
||||||
if ($transformed['balance'] < $transformed['amount']) {
|
|
||||||
$transformed['payments'] = [[
|
|
||||||
'date' => isset($invoice_data['Last Payment Date']) ? date('Y-m-d', strtotime($invoice_data['Invoice Date'])) : date('Y-m-d'),
|
|
||||||
'amount' => $transformed['amount'] - $transformed['balance'],
|
|
||||||
]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $transformed;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1112,6 +1112,9 @@ class CompanyImport implements ShouldQueue
|
|||||||
|
|
||||||
foreach((object)$this->getObject("documents") as $document)
|
foreach((object)$this->getObject("documents") as $document)
|
||||||
{
|
{
|
||||||
|
//todo enable this for v5.5.51
|
||||||
|
if(!$this->transformDocumentId($document->documentable_id, $document->documentable_type))
|
||||||
|
continue;
|
||||||
|
|
||||||
$new_document = new Document();
|
$new_document = new Document();
|
||||||
$new_document->user_id = $this->transformId('users', $document->user_id);
|
$new_document->user_id = $this->transformId('users', $document->user_id);
|
||||||
@ -1152,11 +1155,16 @@ class CompanyImport implements ShouldQueue
|
|||||||
{
|
{
|
||||||
try{
|
try{
|
||||||
Storage::disk(config('filesystems.default'))->put($new_document->url, $file);
|
Storage::disk(config('filesystems.default'))->put($new_document->url, $file);
|
||||||
|
|
||||||
|
$new_document->disk = config('filesystems.default');
|
||||||
|
$new_document->save();
|
||||||
|
|
||||||
}
|
}
|
||||||
catch(\Exception $e)
|
catch(\Exception $e)
|
||||||
{
|
{
|
||||||
nlog($e->getMessage());
|
nlog($e->getMessage());
|
||||||
nlog("I could not upload {$new_document->url}");
|
nlog("I could not upload {$new_document->url}");
|
||||||
|
$new_document->forceDelete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1279,6 +1287,9 @@ class CompanyImport implements ShouldQueue
|
|||||||
case Payment::class:
|
case Payment::class:
|
||||||
return $this->transformId('payments', $id);
|
return $this->transformId('payments', $id);
|
||||||
break;
|
break;
|
||||||
|
case Project::class:
|
||||||
|
return $this->transformId('projects', $id);
|
||||||
|
break;
|
||||||
case Product::class:
|
case Product::class:
|
||||||
return $this->transformId('products', $id);
|
return $this->transformId('products', $id);
|
||||||
break;
|
break;
|
||||||
@ -1294,7 +1305,7 @@ class CompanyImport implements ShouldQueue
|
|||||||
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
# code...
|
return false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,627 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Invoice Ninja (https://invoiceninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Jobs\Import;
|
|
||||||
|
|
||||||
use App\Factory\ClientFactory;
|
|
||||||
use App\Factory\InvoiceFactory;
|
|
||||||
use App\Factory\PaymentFactory;
|
|
||||||
use App\Http\Requests\Invoice\StoreInvoiceRequest;
|
|
||||||
use App\Import\ImportException;
|
|
||||||
use App\Import\Transformers\BaseTransformer;
|
|
||||||
use App\Jobs\Mail\NinjaMailerJob;
|
|
||||||
use App\Jobs\Mail\NinjaMailerObject;
|
|
||||||
use App\Libraries\MultiDB;
|
|
||||||
use App\Mail\Import\ImportCompleted;
|
|
||||||
use App\Models\Client;
|
|
||||||
use App\Models\ClientContact;
|
|
||||||
use App\Models\Company;
|
|
||||||
use App\Models\Country;
|
|
||||||
use App\Models\Currency;
|
|
||||||
use App\Models\ExpenseCategory;
|
|
||||||
use App\Models\Invoice;
|
|
||||||
use App\Models\PaymentType;
|
|
||||||
use App\Models\Product;
|
|
||||||
use App\Models\Project;
|
|
||||||
use App\Models\TaxRate;
|
|
||||||
use App\Models\User;
|
|
||||||
use App\Models\Vendor;
|
|
||||||
use App\Repositories\BaseRepository;
|
|
||||||
use App\Repositories\ClientRepository;
|
|
||||||
use App\Repositories\InvoiceRepository;
|
|
||||||
use App\Repositories\PaymentRepository;
|
|
||||||
use App\Utils\Ninja;
|
|
||||||
use App\Utils\Traits\CleanLineItems;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Support\Facades\App;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
use Illuminate\Support\Facades\Cache;
|
|
||||||
use Illuminate\Support\Facades\Validator;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use League\Csv\Reader;
|
|
||||||
use League\Csv\Statement;
|
|
||||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
|
||||||
|
|
||||||
class CSVImport implements ShouldQueue
|
|
||||||
{
|
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, CleanLineItems;
|
|
||||||
|
|
||||||
public $invoice;
|
|
||||||
|
|
||||||
public $company;
|
|
||||||
|
|
||||||
public $hash;
|
|
||||||
|
|
||||||
public $import_type;
|
|
||||||
|
|
||||||
public $skip_header;
|
|
||||||
|
|
||||||
public $column_map;
|
|
||||||
|
|
||||||
public $import_array;
|
|
||||||
|
|
||||||
public $error_array = [];
|
|
||||||
|
|
||||||
public $maps;
|
|
||||||
|
|
||||||
public function __construct(array $request, Company $company)
|
|
||||||
{
|
|
||||||
$this->company = $company;
|
|
||||||
$this->hash = $request['hash'];
|
|
||||||
$this->import_type = $request['import_type'];
|
|
||||||
$this->skip_header = $request['skip_header'] ?? null;
|
|
||||||
$this->column_map =
|
|
||||||
! empty($request['column_map']) ?
|
|
||||||
array_combine(array_keys($request['column_map']), array_column($request['column_map'], 'mapping')) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the job.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
MultiDB::setDb($this->company->db);
|
|
||||||
|
|
||||||
Auth::login($this->company->owner(), true);
|
|
||||||
|
|
||||||
auth()->user()->setCompany($this->company);
|
|
||||||
|
|
||||||
$this->buildMaps();
|
|
||||||
|
|
||||||
nlog('import '.$this->import_type);
|
|
||||||
foreach (['client', 'product', 'invoice', 'payment', 'vendor', 'expense'] as $entityType) {
|
|
||||||
$csvData = $this->getCsvData($entityType);
|
|
||||||
|
|
||||||
if (! empty($csvData)) {
|
|
||||||
$importFunction = 'import'.Str::plural(Str::title($entityType));
|
|
||||||
$preTransformFunction = 'preTransform'.Str::title($this->import_type);
|
|
||||||
|
|
||||||
if (method_exists($this, $preTransformFunction)) {
|
|
||||||
$csvData = $this->$preTransformFunction($csvData, $entityType);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($csvData)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method_exists($this, $importFunction)) {
|
|
||||||
// If there's an entity-specific import function, use that.
|
|
||||||
$this->$importFunction($csvData);
|
|
||||||
} else {
|
|
||||||
// Otherwise, use the generic import function.
|
|
||||||
$this->importEntities($csvData, $entityType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = [
|
|
||||||
'errors' => $this->error_array,
|
|
||||||
'company' => $this->company,
|
|
||||||
];
|
|
||||||
|
|
||||||
$nmo = new NinjaMailerObject;
|
|
||||||
$nmo->mailable = new ImportCompleted($this->company, $data);
|
|
||||||
$nmo->company = $this->company;
|
|
||||||
$nmo->settings = $this->company->settings;
|
|
||||||
$nmo->to_user = $this->company->owner();
|
|
||||||
|
|
||||||
NinjaMailerJob::dispatch($nmo);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
private function preTransformCsv($csvData, $entityType)
|
|
||||||
{
|
|
||||||
if (empty($this->column_map[$entityType])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->skip_header) {
|
|
||||||
array_shift($csvData);
|
|
||||||
}
|
|
||||||
|
|
||||||
//sort the array by key
|
|
||||||
$keys = $this->column_map[$entityType];
|
|
||||||
ksort($keys);
|
|
||||||
|
|
||||||
$csvData = array_map(function ($row) use ($keys) {
|
|
||||||
return array_combine($keys, array_intersect_key($row, $keys));
|
|
||||||
}, $csvData);
|
|
||||||
|
|
||||||
if ($entityType === 'invoice') {
|
|
||||||
$csvData = $this->groupInvoices($csvData, 'invoice.number');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $csvData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function preTransformFreshbooks($csvData, $entityType)
|
|
||||||
{
|
|
||||||
$csvData = $this->mapCSVHeaderToKeys($csvData);
|
|
||||||
|
|
||||||
if ($entityType === 'invoice') {
|
|
||||||
$csvData = $this->groupInvoices($csvData, 'Invoice #');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $csvData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function preTransformInvoicely($csvData, $entityType)
|
|
||||||
{
|
|
||||||
$csvData = $this->mapCSVHeaderToKeys($csvData);
|
|
||||||
|
|
||||||
return $csvData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function preTransformInvoice2go($csvData, $entityType)
|
|
||||||
{
|
|
||||||
$csvData = $this->mapCSVHeaderToKeys($csvData);
|
|
||||||
|
|
||||||
return $csvData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function preTransformZoho($csvData, $entityType)
|
|
||||||
{
|
|
||||||
$csvData = $this->mapCSVHeaderToKeys($csvData);
|
|
||||||
|
|
||||||
if ($entityType === 'invoice') {
|
|
||||||
$csvData = $this->groupInvoices($csvData, 'Invoice Number');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $csvData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function preTransformWaveaccounting($csvData, $entityType)
|
|
||||||
{
|
|
||||||
$csvData = $this->mapCSVHeaderToKeys($csvData);
|
|
||||||
|
|
||||||
if ($entityType === 'invoice') {
|
|
||||||
$csvData = $this->groupInvoices($csvData, 'Invoice Number');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $csvData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function groupInvoices($csvData, $key)
|
|
||||||
{
|
|
||||||
// Group by invoice.
|
|
||||||
$grouped = [];
|
|
||||||
|
|
||||||
foreach ($csvData as $line_item) {
|
|
||||||
if (empty($line_item[$key])) {
|
|
||||||
$this->error_array['invoice'][] = ['invoice' => $line_item, 'error' => 'No invoice number'];
|
|
||||||
} else {
|
|
||||||
$grouped[$line_item[$key]][] = $line_item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $grouped;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function mapCSVHeaderToKeys($csvData)
|
|
||||||
{
|
|
||||||
$keys = array_shift($csvData);
|
|
||||||
|
|
||||||
return array_map(function ($values) use ($keys) {
|
|
||||||
return array_combine($keys, $values);
|
|
||||||
}, $csvData);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
private function importInvoices($invoices)
|
|
||||||
{
|
|
||||||
$invoice_transformer = $this->getTransformer('invoice');
|
|
||||||
|
|
||||||
/** @var PaymentRepository $payment_repository */
|
|
||||||
$payment_repository = app()->make(PaymentRepository::class);
|
|
||||||
$payment_repository->import_mode = true;
|
|
||||||
|
|
||||||
/** @var ClientRepository $client_repository */
|
|
||||||
$client_repository = app()->make(ClientRepository::class);
|
|
||||||
$client_repository->import_mode = true;
|
|
||||||
|
|
||||||
$invoice_repository = new InvoiceRepository();
|
|
||||||
$invoice_repository->import_mode = true;
|
|
||||||
|
|
||||||
foreach ($invoices as $raw_invoice) {
|
|
||||||
try {
|
|
||||||
$invoice_data = $invoice_transformer->transform($raw_invoice);
|
|
||||||
|
|
||||||
$invoice_data['line_items'] = $this->cleanItems($invoice_data['line_items'] ?? []);
|
|
||||||
|
|
||||||
// If we don't have a client ID, but we do have client data, go ahead and create the client.
|
|
||||||
if (empty($invoice_data['client_id']) && ! empty($invoice_data['client'])) {
|
|
||||||
$client_data = $invoice_data['client'];
|
|
||||||
$client_data['user_id'] = $this->getUserIDForRecord($invoice_data);
|
|
||||||
|
|
||||||
$client_repository->save(
|
|
||||||
$client_data,
|
|
||||||
$client = ClientFactory::create($this->company->id, $client_data['user_id'])
|
|
||||||
);
|
|
||||||
$invoice_data['client_id'] = $client->id;
|
|
||||||
unset($invoice_data['client']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$validator = Validator::make($invoice_data, ( new StoreInvoiceRequest() )->rules());
|
|
||||||
if ($validator->fails()) {
|
|
||||||
$this->error_array['invoice'][] =
|
|
||||||
['invoice' => $invoice_data, 'error' => $validator->errors()->all()];
|
|
||||||
} else {
|
|
||||||
$invoice = InvoiceFactory::create($this->company->id, $this->getUserIDForRecord($invoice_data));
|
|
||||||
if (! empty($invoice_data['status_id'])) {
|
|
||||||
$invoice->status_id = $invoice_data['status_id'];
|
|
||||||
}
|
|
||||||
$invoice_repository->save($invoice_data, $invoice);
|
|
||||||
$this->addInvoiceToMaps($invoice);
|
|
||||||
|
|
||||||
// If we're doing a generic CSV import, only import payment data if we're not importing a payment CSV.
|
|
||||||
// If we're doing a platform-specific import, trust the platform to only return payment info if there's not a separate payment CSV.
|
|
||||||
if ($this->import_type !== 'csv' || empty($this->column_map['payment'])) {
|
|
||||||
// Check for payment columns
|
|
||||||
if (! empty($invoice_data['payments'])) {
|
|
||||||
foreach ($invoice_data['payments'] as $payment_data) {
|
|
||||||
$payment_data['user_id'] = $invoice->user_id;
|
|
||||||
$payment_data['client_id'] = $invoice->client_id;
|
|
||||||
$payment_data['invoices'] = [
|
|
||||||
[
|
|
||||||
'invoice_id' => $invoice->id,
|
|
||||||
'amount' => $payment_data['amount'] ?? null,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
/* Make sure we don't apply any payments to invoices with a Zero Amount*/
|
|
||||||
if ($invoice->amount > 0) {
|
|
||||||
$payment_repository->save(
|
|
||||||
$payment_data,
|
|
||||||
PaymentFactory::create($this->company->id, $invoice->user_id, $invoice->client_id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->actionInvoiceStatus($invoice, $invoice_data, $invoice_repository);
|
|
||||||
}
|
|
||||||
} catch (\Exception $ex) {
|
|
||||||
if ($ex instanceof ImportException) {
|
|
||||||
$message = $ex->getMessage();
|
|
||||||
} else {
|
|
||||||
report($ex);
|
|
||||||
$message = 'Unknown error';
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->error_array['invoice'][] = ['invoice' => $raw_invoice, 'error' => $message];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function actionInvoiceStatus($invoice, $invoice_data, $invoice_repository)
|
|
||||||
{
|
|
||||||
if (! empty($invoice_data['archived'])) {
|
|
||||||
$invoice_repository->archive($invoice);
|
|
||||||
$invoice->fresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! empty($invoice_data['viewed'])) {
|
|
||||||
$invoice = $invoice->service()->markViewed()->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($invoice->status_id === Invoice::STATUS_DRAFT) {
|
|
||||||
} elseif ($invoice->status_id === Invoice::STATUS_SENT) {
|
|
||||||
$invoice = $invoice->service()->markSent()->save();
|
|
||||||
} elseif ($invoice->status_id <= Invoice::STATUS_SENT && $invoice->amount > 0) {
|
|
||||||
if ($invoice->balance <= 0) {
|
|
||||||
$invoice->status_id = Invoice::STATUS_PAID;
|
|
||||||
$invoice->save();
|
|
||||||
} elseif ($invoice->balance != $invoice->amount) {
|
|
||||||
$invoice->status_id = Invoice::STATUS_PARTIAL;
|
|
||||||
$invoice->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $invoice;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function importEntities($records, $entity_type)
|
|
||||||
{
|
|
||||||
$entity_type = Str::slug($entity_type, '_');
|
|
||||||
$formatted_entity_type = Str::title($entity_type);
|
|
||||||
|
|
||||||
$request_name = "\\App\\Http\\Requests\\${formatted_entity_type}\\Store${formatted_entity_type}Request";
|
|
||||||
$repository_name = '\\App\\Repositories\\'.$formatted_entity_type.'Repository';
|
|
||||||
$factoryName = '\\App\\Factory\\'.$formatted_entity_type.'Factory';
|
|
||||||
|
|
||||||
/** @var BaseRepository $repository */
|
|
||||||
$repository = app()->make($repository_name);
|
|
||||||
$repository->import_mode = true;
|
|
||||||
|
|
||||||
$transformer = $this->getTransformer($entity_type);
|
|
||||||
|
|
||||||
foreach ($records as $record) {
|
|
||||||
try {
|
|
||||||
$entity = $transformer->transform($record);
|
|
||||||
|
|
||||||
/** @var \App\Http\Requests\Request $request */
|
|
||||||
// $request = new $request_name();
|
|
||||||
// $request->prepareForValidation();
|
|
||||||
|
|
||||||
// Pass entity data to request so it can be validated
|
|
||||||
// $request->query = $request->request = new ParameterBag( $entity );
|
|
||||||
// $validator = Validator::make( $entity, $request->rules() );
|
|
||||||
$validator = $request_name::runFormRequest($entity);
|
|
||||||
|
|
||||||
if ($validator->fails()) {
|
|
||||||
$this->error_array[$entity_type][] =
|
|
||||||
[$entity_type => $record, 'error' => $validator->errors()->all()];
|
|
||||||
} else {
|
|
||||||
$entity =
|
|
||||||
$repository->save(
|
|
||||||
array_diff_key($entity, ['user_id' => false]),
|
|
||||||
$factoryName::create($this->company->id, $this->getUserIDForRecord($entity)));
|
|
||||||
|
|
||||||
$entity->save();
|
|
||||||
if (method_exists($this, 'add'.$formatted_entity_type.'ToMaps')) {
|
|
||||||
$this->{'add'.$formatted_entity_type.'ToMaps'}($entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (\Exception $ex) {
|
|
||||||
if ($ex instanceof ImportException) {
|
|
||||||
$message = $ex->getMessage();
|
|
||||||
} else {
|
|
||||||
report($ex);
|
|
||||||
$message = 'Unknown error';
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->error_array[$entity_type][] = [$entity_type => $record, 'error' => $message];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $entity_type
|
|
||||||
*
|
|
||||||
* @return BaseTransformer
|
|
||||||
*/
|
|
||||||
private function getTransformer($entity_type)
|
|
||||||
{
|
|
||||||
$formatted_entity_type = Str::title($entity_type);
|
|
||||||
$formatted_import_type = Str::title($this->import_type);
|
|
||||||
$transformer_name =
|
|
||||||
'\\App\\Import\\Transformers\\'.$formatted_import_type.'\\'.$formatted_entity_type.'Transformer';
|
|
||||||
|
|
||||||
return new $transformer_name($this->maps);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
private function buildMaps()
|
|
||||||
{
|
|
||||||
$this->maps = [
|
|
||||||
'company' => $this->company,
|
|
||||||
'client' => [],
|
|
||||||
'contact' => [],
|
|
||||||
'invoice' => [],
|
|
||||||
'invoice_client' => [],
|
|
||||||
'product' => [],
|
|
||||||
'countries' => [],
|
|
||||||
'countries2' => [],
|
|
||||||
'currencies' => [],
|
|
||||||
'client_ids' => [],
|
|
||||||
'invoice_ids' => [],
|
|
||||||
'vendors' => [],
|
|
||||||
'expense_categories' => [],
|
|
||||||
'payment_types' => [],
|
|
||||||
'tax_rates' => [],
|
|
||||||
'tax_names' => [],
|
|
||||||
];
|
|
||||||
|
|
||||||
Client::where('company_id', $this->company->id)->cursor()->each(function ($client) {
|
|
||||||
$this->addClientToMaps($client);
|
|
||||||
});
|
|
||||||
|
|
||||||
ClientContact::where('company_id', $this->company->id)->cursor()->each(function ($contact) {
|
|
||||||
$this->addContactToMaps($contact);
|
|
||||||
});
|
|
||||||
|
|
||||||
Invoice::where('company_id', $this->company->id)->cursor()->each(function ($invoice) {
|
|
||||||
$this->addInvoiceToMaps($invoice);
|
|
||||||
});
|
|
||||||
|
|
||||||
Product::where('company_id', $this->company->id)->cursor()->each(function ($product) {
|
|
||||||
$this->addProductToMaps($product);
|
|
||||||
});
|
|
||||||
|
|
||||||
Project::where('company_id', $this->company->id)->cursor()->each(function ($project) {
|
|
||||||
$this->addProjectToMaps($project);
|
|
||||||
});
|
|
||||||
|
|
||||||
Country::all()->each(function ($country) {
|
|
||||||
$this->maps['countries'][strtolower($country->name)] = $country->id;
|
|
||||||
$this->maps['countries2'][strtolower($country->iso_3166_2)] = $country->id;
|
|
||||||
});
|
|
||||||
|
|
||||||
Currency::all()->each(function ($currency) {
|
|
||||||
$this->maps['currencies'][strtolower($currency->code)] = $currency->id;
|
|
||||||
});
|
|
||||||
|
|
||||||
PaymentType::all()->each(function ($payment_type) {
|
|
||||||
$this->maps['payment_types'][strtolower($payment_type->name)] = $payment_type->id;
|
|
||||||
});
|
|
||||||
|
|
||||||
Vendor::where('company_id', $this->company->id)->cursor()->each(function ($vendor) {
|
|
||||||
$this->addVendorToMaps($vendor);
|
|
||||||
});
|
|
||||||
|
|
||||||
ExpenseCategory::where('company_id', $this->company->id)->cursor()->each(function ($category) {
|
|
||||||
$this->addExpenseCategoryToMaps($category);
|
|
||||||
});
|
|
||||||
|
|
||||||
TaxRate::where('company_id', $this->company->id)->cursor()->each(function ($taxRate) {
|
|
||||||
$name = trim(strtolower($taxRate->name));
|
|
||||||
$this->maps['tax_rates'][$name] = $taxRate->rate;
|
|
||||||
$this->maps['tax_names'][$name] = $taxRate->name;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Invoice $invoice
|
|
||||||
*/
|
|
||||||
private function addInvoiceToMaps(Invoice $invoice)
|
|
||||||
{
|
|
||||||
if ($number = strtolower(trim($invoice->number))) {
|
|
||||||
$this->maps['invoices'][$number] = $invoice;
|
|
||||||
$this->maps['invoice'][$number] = $invoice->id;
|
|
||||||
$this->maps['invoice_client'][$number] = $invoice->client_id;
|
|
||||||
$this->maps['invoice_ids'][$invoice->public_id] = $invoice->id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Client $client
|
|
||||||
*/
|
|
||||||
private function addClientToMaps(Client $client)
|
|
||||||
{
|
|
||||||
if ($name = strtolower(trim($client->name))) {
|
|
||||||
$this->maps['client'][$name] = $client->id;
|
|
||||||
$this->maps['client_ids'][$client->public_id] = $client->id;
|
|
||||||
}
|
|
||||||
if ($client->contacts->count()) {
|
|
||||||
$contact = $client->contacts[0];
|
|
||||||
if ($email = strtolower(trim($contact->email))) {
|
|
||||||
$this->maps['client'][$email] = $client->id;
|
|
||||||
}
|
|
||||||
if ($name = strtolower(trim($contact->first_name.' '.$contact->last_name))) {
|
|
||||||
$this->maps['client'][$name] = $client->id;
|
|
||||||
}
|
|
||||||
$this->maps['client_ids'][$client->public_id] = $client->id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ClientContact $contact
|
|
||||||
*/
|
|
||||||
private function addContactToMaps(ClientContact $contact)
|
|
||||||
{
|
|
||||||
if ($key = strtolower(trim($contact->email))) {
|
|
||||||
$this->maps['contact'][$key] = $contact;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Product $product
|
|
||||||
*/
|
|
||||||
private function addProductToMaps(Product $product)
|
|
||||||
{
|
|
||||||
if ($key = strtolower(trim($product->product_key))) {
|
|
||||||
$this->maps['product'][$key] = $product;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Project $project
|
|
||||||
*/
|
|
||||||
private function addProjectToMaps(Project $project)
|
|
||||||
{
|
|
||||||
if ($key = strtolower(trim($project->name))) {
|
|
||||||
$this->maps['project'][$key] = $project;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addVendorToMaps(Vendor $vendor)
|
|
||||||
{
|
|
||||||
$this->maps['vendor'][strtolower($vendor->name)] = $vendor->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addExpenseCategoryToMaps(ExpenseCategory $category)
|
|
||||||
{
|
|
||||||
if ($name = strtolower($category->name)) {
|
|
||||||
$this->maps['expense_category'][$name] = $category->id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getUserIDForRecord($record)
|
|
||||||
{
|
|
||||||
if (! empty($record['user_id'])) {
|
|
||||||
return $this->findUser($record['user_id']);
|
|
||||||
} else {
|
|
||||||
return $this->company->owner()->id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function findUser($user_hash)
|
|
||||||
{
|
|
||||||
$user = User::where('account_id', $this->company->account->id)
|
|
||||||
->where(\DB::raw('CONCAT_WS(" ", first_name, last_name)'), 'like', '%'.$user_hash.'%')
|
|
||||||
->first();
|
|
||||||
|
|
||||||
if ($user) {
|
|
||||||
return $user->id;
|
|
||||||
} else {
|
|
||||||
return $this->company->owner()->id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getCsvData($entityType)
|
|
||||||
{
|
|
||||||
$base64_encoded_csv = Cache::pull($this->hash.'-'.$entityType);
|
|
||||||
if (empty($base64_encoded_csv)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$csv = base64_decode($base64_encoded_csv);
|
|
||||||
$csv = Reader::createFromString($csv);
|
|
||||||
|
|
||||||
$stmt = new Statement();
|
|
||||||
$data = iterator_to_array($stmt->process($csv));
|
|
||||||
|
|
||||||
if (count($data) > 0) {
|
|
||||||
$headers = $data[0];
|
|
||||||
|
|
||||||
// Remove Invoice Ninja headers
|
|
||||||
if (count($headers) && count($data) > 4 && $this->import_type === 'csv') {
|
|
||||||
$firstCell = $headers[0];
|
|
||||||
if (strstr($firstCell, config('ninja.app_name'))) {
|
|
||||||
array_shift($data); // Invoice Ninja...
|
|
||||||
array_shift($data); // <blank line>
|
|
||||||
array_shift($data); // Enitty Type Header
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
}
|
|
@ -23,7 +23,6 @@ use App\Libraries\MultiDB;
|
|||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
use App\Models\Vendor;
|
use App\Models\Vendor;
|
||||||
use App\Utils\Ninja;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
@ -106,6 +105,24 @@ class CSVIngest implements ShouldQueue
|
|||||||
$new_contact->is_primary = true;
|
$new_contact->is_primary = true;
|
||||||
$new_contact->save();
|
$new_contact->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Client::with('contacts')->where('company_id', $this->company->id)->cursor()->each(function ($client){
|
||||||
|
|
||||||
|
$contact = $client->contacts()->first();
|
||||||
|
$contact->is_primary = true;
|
||||||
|
$contact->save();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Vendor::with('contacts')->where('company_id', $this->company->id)->cursor()->each(function ($vendor){
|
||||||
|
|
||||||
|
$contact = $vendor->contacts()->first();
|
||||||
|
$contact->is_primary = true;
|
||||||
|
$contact->save();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function bootEngine()
|
private function bootEngine()
|
||||||
|
54
app/Jobs/Invoice/CheckGatewayFee.php
Normal file
54
app/Jobs/Invoice/CheckGatewayFee.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Jobs\Invoice;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class CheckGatewayFee implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public $tries = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $invoice_id
|
||||||
|
* @param string $db
|
||||||
|
*/
|
||||||
|
public function __construct(public int $invoice_id, public string $db){}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
MultiDB::setDb($this->db);
|
||||||
|
|
||||||
|
$i = Invoice::withTrashed()->find($this->invoice_id);
|
||||||
|
|
||||||
|
if(!$i)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if($i->status_id == Invoice::STATUS_SENT)
|
||||||
|
{
|
||||||
|
$i->service()->removeUnpaidGatewayFees();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
111
app/Jobs/Invoice/InvoiceCheckLateWebhook.php
Normal file
111
app/Jobs/Invoice/InvoiceCheckLateWebhook.php
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Jobs\Invoice;
|
||||||
|
|
||||||
|
use App\Jobs\Util\WebhookHandler;
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\Webhook;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class InvoiceCheckLateWebhook implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*/
|
||||||
|
public function __construct() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
nlog("sending overdue webhooks for invoices");
|
||||||
|
|
||||||
|
if (! config('ninja.db.multi_db_enabled')){
|
||||||
|
|
||||||
|
$company_ids = Webhook::where('event_id', Webhook::EVENT_LATE_INVOICE)
|
||||||
|
->where('is_deleted', 0)
|
||||||
|
->pluck('company_id');
|
||||||
|
|
||||||
|
Invoice::query()
|
||||||
|
->where('is_deleted', false)
|
||||||
|
->whereNull('deleted_at')
|
||||||
|
->whereNotNull('due_date')
|
||||||
|
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||||
|
->where('balance', '>', 0)
|
||||||
|
->whereIn('company_id', $company_ids)
|
||||||
|
->whereHas('client', function ($query) {
|
||||||
|
$query->where('is_deleted', 0)
|
||||||
|
->where('deleted_at', null);
|
||||||
|
})
|
||||||
|
->whereHas('company', function ($query){
|
||||||
|
$query->where('is_disabled', 0);
|
||||||
|
})
|
||||||
|
->whereBetween('due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
|
||||||
|
->cursor()
|
||||||
|
->each(function ($invoice){
|
||||||
|
|
||||||
|
WebhookHandler::dispatch(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company, 'client')->delay(now()->addSeconds(2));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
foreach (MultiDB::$dbs as $db)
|
||||||
|
{
|
||||||
|
|
||||||
|
MultiDB::setDB($db);
|
||||||
|
|
||||||
|
$company_ids = Webhook::where('event_id', Webhook::EVENT_LATE_INVOICE)
|
||||||
|
->where('is_deleted', 0)
|
||||||
|
->pluck('company_id');
|
||||||
|
|
||||||
|
Invoice::query()
|
||||||
|
->where('is_deleted', false)
|
||||||
|
->whereNull('deleted_at')
|
||||||
|
->whereNotNull('due_date')
|
||||||
|
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||||
|
->where('balance', '>', 0)
|
||||||
|
->whereIn('company_id', $company_ids)
|
||||||
|
->whereHas('client', function ($query) {
|
||||||
|
$query->where('is_deleted', 0)
|
||||||
|
->where('deleted_at', null);
|
||||||
|
})
|
||||||
|
->whereHas('company', function ($query){
|
||||||
|
$query->where('is_disabled', 0);
|
||||||
|
})
|
||||||
|
->whereBetween('due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
|
||||||
|
->cursor()
|
||||||
|
->each(function ($invoice){
|
||||||
|
|
||||||
|
WebhookHandler::dispatch(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company, 'client')->delay(now()->addSeconds(2));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -34,6 +34,7 @@ use GuzzleHttp\Exception\ClientException;
|
|||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Mail\Mailer;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
@ -63,10 +64,18 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
|
|
||||||
private $mailer;
|
private $mailer;
|
||||||
|
|
||||||
|
protected $client_postmark_secret = false;
|
||||||
|
|
||||||
|
protected $client_mailgun_secret = false;
|
||||||
|
|
||||||
|
protected $client_mailgun_domain = false;
|
||||||
|
|
||||||
|
|
||||||
public function __construct(NinjaMailerObject $nmo, bool $override = false)
|
public function __construct(NinjaMailerObject $nmo, bool $override = false)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->nmo = $nmo;
|
$this->nmo = $nmo;
|
||||||
|
|
||||||
$this->override = $override;
|
$this->override = $override;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -80,12 +89,14 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
/* Serializing models from other jobs wipes the primary key */
|
/* Serializing models from other jobs wipes the primary key */
|
||||||
$this->company = Company::where('company_key', $this->nmo->company->company_key)->first();
|
$this->company = Company::where('company_key', $this->nmo->company->company_key)->first();
|
||||||
|
|
||||||
|
/* If any pre conditions fail, we return early here */
|
||||||
if($this->preFlightChecksFail())
|
if($this->preFlightChecksFail())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Set the email driver */
|
/* Set the email driver */
|
||||||
$this->setMailDriver();
|
$this->setMailDriver();
|
||||||
|
|
||||||
|
/* Run time we set Reply To Email*/
|
||||||
if (strlen($this->nmo->settings->reply_to_email) > 1) {
|
if (strlen($this->nmo->settings->reply_to_email) > 1) {
|
||||||
|
|
||||||
if(property_exists($this->nmo->settings, 'reply_to_name'))
|
if(property_exists($this->nmo->settings, 'reply_to_name'))
|
||||||
@ -100,8 +111,10 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
$this->nmo->mailable->replyTo($this->company->owner()->email, $this->company->owner()->present()->name());
|
$this->nmo->mailable->replyTo($this->company->owner()->email, $this->company->owner()->present()->name());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Run time we set the email tag */
|
||||||
$this->nmo->mailable->tag($this->company->company_key);
|
$this->nmo->mailable->tag($this->company->company_key);
|
||||||
|
|
||||||
|
/* If we have an invitation present, we pass the invitation key into the email headers*/
|
||||||
if($this->nmo->invitation)
|
if($this->nmo->invitation)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -119,7 +132,19 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
nlog("trying to send to {$this->nmo->to_user->email} ". now()->toDateTimeString());
|
nlog("trying to send to {$this->nmo->to_user->email} ". now()->toDateTimeString());
|
||||||
nlog("Using mailer => ". $this->mailer);
|
nlog("Using mailer => ". $this->mailer);
|
||||||
|
|
||||||
Mail::mailer($this->mailer)
|
$mailer = Mail::mailer($this->mailer);
|
||||||
|
|
||||||
|
if($this->client_postmark_secret){
|
||||||
|
nlog("inside postmark config");
|
||||||
|
nlog($this->client_postmark_secret);
|
||||||
|
$mailer->postmark_config($this->client_postmark_secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->client_mailgun_secret){
|
||||||
|
$mailer->mailgun_config($this->client_mailgun_secret, $this->client_mailgun_domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
$mailer
|
||||||
->to($this->nmo->to_user->email)
|
->to($this->nmo->to_user->email)
|
||||||
->send($this->nmo->mailable);
|
->send($this->nmo->mailable);
|
||||||
|
|
||||||
@ -136,6 +161,8 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
|
|
||||||
nlog("error failed with {$e->getMessage()}");
|
nlog("error failed with {$e->getMessage()}");
|
||||||
|
|
||||||
|
$this->cleanUpMailers();
|
||||||
|
|
||||||
$message = $e->getMessage();
|
$message = $e->getMessage();
|
||||||
|
|
||||||
if($e instanceof \Google\Service\Exception){
|
if($e instanceof \Google\Service\Exception){
|
||||||
@ -176,12 +203,18 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//always dump the drivers to prevent reuse
|
||||||
|
$this->cleanUpMailers();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Switch statement to handle failure notifications */
|
/**
|
||||||
private function entityEmailFailed($message)
|
* Entity notification when an email fails to send
|
||||||
|
*
|
||||||
|
* @param string $message
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function entityEmailFailed($message): void
|
||||||
{
|
{
|
||||||
$class = get_class($this->nmo->entity);
|
$class = get_class($this->nmo->entity);
|
||||||
|
|
||||||
@ -202,6 +235,9 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the configured Mailer
|
||||||
|
*/
|
||||||
private function setMailDriver()
|
private function setMailDriver()
|
||||||
{
|
{
|
||||||
/* Singletons need to be rebooted each time just in case our Locale is changing*/
|
/* Singletons need to be rebooted each time just in case our Locale is changing*/
|
||||||
@ -216,23 +252,34 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
case 'gmail':
|
case 'gmail':
|
||||||
$this->mailer = 'gmail';
|
$this->mailer = 'gmail';
|
||||||
$this->setGmailMailer();
|
$this->setGmailMailer();
|
||||||
break;
|
return;
|
||||||
case 'office365':
|
case 'office365':
|
||||||
$this->mailer = 'office365';
|
$this->mailer = 'office365';
|
||||||
$this->setOfficeMailer();
|
$this->setOfficeMailer();
|
||||||
break;
|
return;
|
||||||
|
case 'client_postmark':
|
||||||
|
$this->mailer = 'postmark';
|
||||||
|
$this->setPostmarkMailer();
|
||||||
|
return;
|
||||||
|
case 'client_mailgun':
|
||||||
|
$this->mailer = 'mailgun';
|
||||||
|
$this->setMailgunMailer();
|
||||||
|
return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if(Ninja::isSelfHost())
|
if(Ninja::isSelfHost())
|
||||||
$this->setSelfHostMultiMailer();
|
$this->setSelfHostMultiMailer();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function setSelfHostMultiMailer()
|
/**
|
||||||
|
* Allows configuration of multiple mailers
|
||||||
|
* per company for use by self hosted users
|
||||||
|
*/
|
||||||
|
private function setSelfHostMultiMailer(): void
|
||||||
{
|
{
|
||||||
|
|
||||||
if (env($this->company->id . '_MAIL_HOST'))
|
if (env($this->company->id . '_MAIL_HOST'))
|
||||||
@ -259,20 +306,116 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
private function setOfficeMailer()
|
* Ensure we discard any data that is not required
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function cleanUpMailers(): void
|
||||||
{
|
{
|
||||||
$sending_user = $this->nmo->settings->gmail_sending_user_id;
|
$this->client_postmark_secret = false;
|
||||||
|
|
||||||
$user = User::find($this->decodePrimaryKey($sending_user));
|
$this->client_mailgun_secret = false;
|
||||||
|
|
||||||
|
$this->client_mailgun_domain = false;
|
||||||
|
|
||||||
|
//always dump the drivers to prevent reuse
|
||||||
|
app('mail.manager')->forgetMailers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to ensure no cross account
|
||||||
|
* emails can be sent.
|
||||||
|
*
|
||||||
|
* @param User $user
|
||||||
|
*/
|
||||||
|
private function checkValidSendingUser($user)
|
||||||
|
{
|
||||||
/* Always ensure the user is set on the correct account */
|
/* Always ensure the user is set on the correct account */
|
||||||
if($user->account_id != $this->company->account_id){
|
if($user->account_id != $this->company->account_id){
|
||||||
|
|
||||||
$this->nmo->settings->email_sending_method = 'default';
|
$this->nmo->settings->email_sending_method = 'default';
|
||||||
return $this->setMailDriver();
|
return $this->setMailDriver();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the sending user
|
||||||
|
* when configuring the Mailer
|
||||||
|
* on behalf of the client
|
||||||
|
*
|
||||||
|
* @return User $user
|
||||||
|
*/
|
||||||
|
private function resolveSendingUser(): ?User
|
||||||
|
{
|
||||||
|
$sending_user = $this->nmo->settings->gmail_sending_user_id;
|
||||||
|
|
||||||
|
$user = User::find($this->decodePrimaryKey($sending_user));
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Mailgun using client supplied secret
|
||||||
|
* as the Mailer
|
||||||
|
*/
|
||||||
|
private function setMailgunMailer()
|
||||||
|
{
|
||||||
|
if(strlen($this->nmo->settings->mailgun_secret) > 2 && strlen($this->nmo->settings->mailgun_domain) > 2){
|
||||||
|
$this->client_mailgun_secret = $this->nmo->settings->mailgun_secret;
|
||||||
|
$this->client_mailgun_domain = $this->nmo->settings->mailgun_domain;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$this->nmo->settings->email_sending_method = 'default';
|
||||||
|
return $this->setMailDriver();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$user = $this->resolveSendingUser();
|
||||||
|
|
||||||
|
$this->nmo
|
||||||
|
->mailable
|
||||||
|
->from($user->email, $user->name());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Postmark using client supplied secret
|
||||||
|
* as the Mailer
|
||||||
|
*/
|
||||||
|
private function setPostmarkMailer()
|
||||||
|
{
|
||||||
|
if(strlen($this->nmo->settings->postmark_secret) > 2){
|
||||||
|
$this->client_postmark_secret = $this->nmo->settings->postmark_secret;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$this->nmo->settings->email_sending_method = 'default';
|
||||||
|
return $this->setMailDriver();
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $this->resolveSendingUser();
|
||||||
|
|
||||||
|
$this->nmo
|
||||||
|
->mailable
|
||||||
|
->from($user->email, $user->name());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Microsoft via Oauth
|
||||||
|
* as the Mailer
|
||||||
|
*/
|
||||||
|
private function setOfficeMailer()
|
||||||
|
{
|
||||||
|
$user = $this->resolveSendingUser();
|
||||||
|
|
||||||
|
/* Always ensure the user is set on the correct account */
|
||||||
|
// if($user->account_id != $this->company->account_id){
|
||||||
|
|
||||||
|
// $this->nmo->settings->email_sending_method = 'default';
|
||||||
|
// return $this->setMailDriver();
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
$this->checkValidSendingUser($user);
|
||||||
|
|
||||||
nlog("Sending via {$user->name()}");
|
nlog("Sending via {$user->name()}");
|
||||||
|
|
||||||
@ -301,20 +444,26 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
sleep(rand(1,3));
|
sleep(rand(1,3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures GMail via Oauth
|
||||||
|
* as the Mailer
|
||||||
|
*/
|
||||||
private function setGmailMailer()
|
private function setGmailMailer()
|
||||||
{
|
{
|
||||||
|
|
||||||
$sending_user = $this->nmo->settings->gmail_sending_user_id;
|
$user = $this->resolveSendingUser();
|
||||||
|
|
||||||
$user = User::find($this->decodePrimaryKey($sending_user));
|
$this->checkValidSendingUser($user);
|
||||||
|
|
||||||
/* Always ensure the user is set on the correct account */
|
/* Always ensure the user is set on the correct account */
|
||||||
if($user->account_id != $this->company->account_id){
|
// if($user->account_id != $this->company->account_id){
|
||||||
|
|
||||||
$this->nmo->settings->email_sending_method = 'default';
|
// $this->nmo->settings->email_sending_method = 'default';
|
||||||
return $this->setMailDriver();
|
// return $this->setMailDriver();
|
||||||
|
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
$this->checkValidSendingUser($user);
|
||||||
|
|
||||||
nlog("Sending via {$user->name()}");
|
nlog("Sending via {$user->name()}");
|
||||||
|
|
||||||
@ -370,7 +519,14 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function preFlightChecksFail()
|
/**
|
||||||
|
* On the hosted platform we scan all outbound email for
|
||||||
|
* spam. This sequence processes the filters we use on all
|
||||||
|
* emails.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function preFlightChecksFail(): bool
|
||||||
{
|
{
|
||||||
|
|
||||||
/* If we are migrating data we don't want to fire any emails */
|
/* If we are migrating data we don't want to fire any emails */
|
||||||
@ -396,9 +552,11 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
/* If the account is verified, we allow emails to flow */
|
/* If the account is verified, we allow emails to flow */
|
||||||
if(Ninja::isHosted() && $this->company->account && $this->company->account->is_verified_account) {
|
if(Ninja::isHosted() && $this->company->account && $this->company->account->is_verified_account) {
|
||||||
|
|
||||||
|
//11-01-2022
|
||||||
|
|
||||||
/* Continue to analyse verified accounts in case they later start sending poor quality emails*/
|
/* Continue to analyse verified accounts in case they later start sending poor quality emails*/
|
||||||
if(class_exists(\Modules\Admin\Jobs\Account\EmailQuality::class))
|
// if(class_exists(\Modules\Admin\Jobs\Account\EmailQuality::class))
|
||||||
(new \Modules\Admin\Jobs\Account\EmailQuality($this->nmo, $this->company))->run();
|
// (new \Modules\Admin\Jobs\Account\EmailQuality($this->nmo, $this->company))->run();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -423,7 +581,14 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function logMailError($errors, $recipient_object)
|
/**
|
||||||
|
* Logs any errors to the SystemLog
|
||||||
|
*
|
||||||
|
* @param string $errors
|
||||||
|
* @param App\Models\User | App\Models\Client $recipient_object
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function logMailError($errors, $recipient_object) :void
|
||||||
{
|
{
|
||||||
|
|
||||||
(new SystemLogger(
|
(new SystemLogger(
|
||||||
@ -446,11 +611,12 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function failed($exception = null)
|
/**
|
||||||
{
|
* Attempts to refresh the Microsoft refreshToken
|
||||||
|
*
|
||||||
}
|
* @param App\Models\User
|
||||||
|
* @return string | boool
|
||||||
|
*/
|
||||||
private function refreshOfficeToken($user)
|
private function refreshOfficeToken($user)
|
||||||
{
|
{
|
||||||
$expiry = $user->oauth_user_token_expiry ?: now()->subDay();
|
$expiry = $user->oauth_user_token_expiry ?: now()->subDay();
|
||||||
@ -470,8 +636,6 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
],
|
],
|
||||||
])->getBody()->getContents());
|
])->getBody()->getContents());
|
||||||
|
|
||||||
nlog($token);
|
|
||||||
|
|
||||||
if($token){
|
if($token){
|
||||||
|
|
||||||
$user->oauth_user_refresh_token = property_exists($token, 'refresh_token') ? $token->refresh_token : $user->oauth_user_refresh_token;
|
$user->oauth_user_refresh_token = property_exists($token, 'refresh_token') ? $token->refresh_token : $user->oauth_user_refresh_token;
|
||||||
@ -489,6 +653,11 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function failed($exception = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is this the cleanest way to requeue a job?
|
* Is this the cleanest way to requeue a job?
|
||||||
*
|
*
|
||||||
|
@ -31,9 +31,11 @@ class NinjaMailerObject
|
|||||||
/* Variable for cascading notifications */
|
/* Variable for cascading notifications */
|
||||||
public $entity_string = false;
|
public $entity_string = false;
|
||||||
|
|
||||||
|
/* @var bool | App\Models\InvoiceInvitation | app\Models\QuoteInvitation | app\Models\CreditInvitation | app\Models\RecurringInvoiceInvitation | app\Models\PurchaseOrderInvitation $invitation*/
|
||||||
public $invitation = false;
|
public $invitation = false;
|
||||||
|
|
||||||
public $template = false;
|
public $template = false;
|
||||||
|
|
||||||
|
/* @var bool | App\Models\Invoice | app\Models\Quote | app\Models\Credit | app\Models\RecurringInvoice | app\Models\PurchaseOrder $invitation*/
|
||||||
public $entity = false;
|
public $entity = false;
|
||||||
}
|
}
|
||||||
|
80
app/Jobs/Subscription/CleanStaleInvoiceOrder.php
Normal file
80
app/Jobs/Subscription/CleanStaleInvoiceOrder.php
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Jobs\Subscription;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Repositories\InvoiceRepository;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class CleanStaleInvoiceOrder implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @param int invoice_id
|
||||||
|
* @param string $db
|
||||||
|
*/
|
||||||
|
public function __construct(){}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param InvoiceRepository $repo
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle(InvoiceRepository $repo) : void
|
||||||
|
{
|
||||||
|
|
||||||
|
if (! config('ninja.db.multi_db_enabled')) {
|
||||||
|
|
||||||
|
Invoice::query()
|
||||||
|
->withTrashed()
|
||||||
|
->where('is_proforma', 1)
|
||||||
|
->where('created_at', '<', now()->subHour())
|
||||||
|
->cursor()
|
||||||
|
->each(function ($invoice) use ($repo) {
|
||||||
|
$invoice->is_proforma = false;
|
||||||
|
$repo->delete($invoice);
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
foreach (MultiDB::$dbs as $db)
|
||||||
|
{
|
||||||
|
|
||||||
|
MultiDB::setDB($db);
|
||||||
|
|
||||||
|
Invoice::query()
|
||||||
|
->withTrashed()
|
||||||
|
->where('is_proforma', 1)
|
||||||
|
->where('created_at', '<', now()->subHour())
|
||||||
|
->cursor()
|
||||||
|
->each(function ($invoice) use ($repo) {
|
||||||
|
$invoice->is_proforma = false;
|
||||||
|
$repo->delete($invoice);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function failed($exception = null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -349,6 +349,26 @@ class Import implements ShouldQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
$account = $this->company->account;
|
$account = $this->company->account;
|
||||||
|
|
||||||
|
/* If the user has upgraded their account, do not wipe their payment plan*/
|
||||||
|
if($account->isPaid() || (isset($data['plan']) && $data['plan'] == 'white_label'))
|
||||||
|
{
|
||||||
|
if(isset($data['plan']))
|
||||||
|
unset($data['plan']);
|
||||||
|
|
||||||
|
if(isset($data['plan_term']))
|
||||||
|
unset($data['plan_term']);
|
||||||
|
|
||||||
|
if(isset($data['plan_paid']))
|
||||||
|
unset($data['plan_paid']);
|
||||||
|
|
||||||
|
if(isset($data['plan_started']))
|
||||||
|
unset($data['plan_started']);
|
||||||
|
|
||||||
|
if(isset($data['plan_expires']))
|
||||||
|
unset($data['plan_expires']);
|
||||||
|
}
|
||||||
|
|
||||||
$account->fill($data);
|
$account->fill($data);
|
||||||
$account->save();
|
$account->save();
|
||||||
|
|
||||||
|
@ -21,12 +21,10 @@ use App\Notifications\Admin\EntitySentNotification;
|
|||||||
use App\Utils\Traits\Notifications\UserNotifies;
|
use App\Utils\Traits\Notifications\UserNotifies;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
|
||||||
class PurchaseOrderAcceptedNotification implements ShouldQueue
|
class PurchaseOrderAcceptedListener implements ShouldQueue
|
||||||
{
|
{
|
||||||
use UserNotifies;
|
use UserNotifies;
|
||||||
|
|
||||||
public $delay = 5;
|
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
}
|
}
|
81
app/Listeners/PurchaseOrder/PurchaseOrderCreatedListener.php
Normal file
81
app/Listeners/PurchaseOrder/PurchaseOrderCreatedListener.php
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* PurchaseOrder Ninja (https://purchase_orderninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/purchase_orderninja/purchase_orderninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022. PurchaseOrder Ninja LLC (https://purchase_orderninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Listeners\PurchaseOrder;
|
||||||
|
|
||||||
|
use App\Events\PurchaseOrder\PurchaseOrderWasCreated;
|
||||||
|
use App\Jobs\Mail\NinjaMailer;
|
||||||
|
use App\Jobs\Mail\NinjaMailerJob;
|
||||||
|
use App\Jobs\Mail\NinjaMailerObject;
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Mail\Admin\EntityCreatedObject;
|
||||||
|
use App\Notifications\Admin\EntitySentNotification;
|
||||||
|
use App\Utils\Traits\Notifications\UserNotifies;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
|
||||||
|
class PurchaseOrderCreatedListener implements ShouldQueue
|
||||||
|
{
|
||||||
|
use UserNotifies;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the event.
|
||||||
|
*
|
||||||
|
* @param object $event
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle(PurchaseOrderWasCreated $event)
|
||||||
|
{
|
||||||
|
|
||||||
|
MultiDB::setDb($event->company->db);
|
||||||
|
|
||||||
|
$first_notification_sent = true;
|
||||||
|
|
||||||
|
$purchase_order = $event->purchase_order;
|
||||||
|
|
||||||
|
$nmo = new NinjaMailerObject;
|
||||||
|
$nmo->mailable = new NinjaMailer((new EntityCreatedObject($purchase_order, 'purchase_order'))->build());
|
||||||
|
$nmo->company = $purchase_order->company;
|
||||||
|
$nmo->settings = $purchase_order->company->settings;
|
||||||
|
|
||||||
|
/* We loop through each user and determine whether they need to be notified */
|
||||||
|
foreach ($event->company->company_users as $company_user) {
|
||||||
|
|
||||||
|
/* The User */
|
||||||
|
$user = $company_user->user;
|
||||||
|
|
||||||
|
if (! $user) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is only here to handle the alternate message channels - ie Slack */
|
||||||
|
// $notification = new EntitySentNotification($event->invitation, 'purchase_order');
|
||||||
|
|
||||||
|
/* Returns an array of notification methods */
|
||||||
|
$methods = $this->findUserNotificationTypes($purchase_order->invitations()->first(), $company_user, 'purchase_order', ['all_notifications', 'purchase_order_created', 'purchase_order_created_all']);
|
||||||
|
/* If one of the methods is email then we fire the EntitySentMailer */
|
||||||
|
|
||||||
|
if (($key = array_search('mail', $methods)) !== false) {
|
||||||
|
unset($methods[$key]);
|
||||||
|
|
||||||
|
$nmo->to_user = $user;
|
||||||
|
|
||||||
|
NinjaMailerJob::dispatch($nmo);
|
||||||
|
|
||||||
|
/* This prevents more than one notification being sent */
|
||||||
|
$first_notification_sent = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* PurchaseOrder Ninja (https://purchase_orderninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/purchase_orderninja/purchase_orderninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022. PurchaseOrder Ninja LLC (https://purchase_orderninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Listeners\PurchaseOrder;
|
||||||
|
|
||||||
|
use App\Jobs\Mail\NinjaMailer;
|
||||||
|
use App\Jobs\Mail\NinjaMailerJob;
|
||||||
|
use App\Jobs\Mail\NinjaMailerObject;
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Mail\Admin\EntitySentObject;
|
||||||
|
use App\Notifications\Admin\EntitySentNotification;
|
||||||
|
use App\Utils\Traits\Notifications\UserNotifies;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
|
||||||
|
class PurchaseOrderEmailedNotification implements ShouldQueue
|
||||||
|
{
|
||||||
|
use UserNotifies;
|
||||||
|
|
||||||
|
public $delay = 5;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the event.
|
||||||
|
*
|
||||||
|
* @param object $event
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle($event)
|
||||||
|
{
|
||||||
|
MultiDB::setDb($event->company->db);
|
||||||
|
|
||||||
|
$first_notification_sent = true;
|
||||||
|
|
||||||
|
$purchase_order = $event->invitation->purchase_order->fresh();
|
||||||
|
$purchase_order->last_sent_date = now();
|
||||||
|
$purchase_order->saveQuietly();
|
||||||
|
|
||||||
|
$nmo = new NinjaMailerObject;
|
||||||
|
$nmo->mailable = new NinjaMailer((new EntitySentObject($event->invitation, 'purchase_order', 'purchase_order'))->build());
|
||||||
|
$nmo->company = $purchase_order->company;
|
||||||
|
$nmo->settings = $purchase_order->company->settings;
|
||||||
|
|
||||||
|
/* We loop through each user and determine whether they need to be notified */
|
||||||
|
foreach ($event->invitation->company->company_users as $company_user) {
|
||||||
|
|
||||||
|
/* The User */
|
||||||
|
$user = $company_user->user;
|
||||||
|
|
||||||
|
/* This is only here to handle the alternate message channels - ie Slack */
|
||||||
|
// $notification = new EntitySentNotification($event->invitation, 'purchase_order');
|
||||||
|
|
||||||
|
/* Returns an array of notification methods */
|
||||||
|
$methods = $this->findUserNotificationTypes($event->invitation, $company_user, 'purchase_order', ['all_notifications', 'purchase_order_sent', 'purchase_order_sent_all']);
|
||||||
|
|
||||||
|
/* If one of the methods is email then we fire the EntitySentMailer */
|
||||||
|
if (($key = array_search('mail', $methods)) !== false) {
|
||||||
|
unset($methods[$key]);
|
||||||
|
|
||||||
|
$nmo->to_user = $user;
|
||||||
|
|
||||||
|
NinjaMailerJob::dispatch($nmo);
|
||||||
|
|
||||||
|
/* This prevents more than one notification being sent */
|
||||||
|
$first_notification_sent = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Override the methods in the Notification Class */
|
||||||
|
// $notification->method = $methods;
|
||||||
|
|
||||||
|
// Notify on the alternate channels
|
||||||
|
// $user->notify($notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -64,7 +64,7 @@ class AutoBillingFailureObject
|
|||||||
/* Set customized translations _NOW_ */
|
/* Set customized translations _NOW_ */
|
||||||
$t->replace(Ninja::transformTranslations($this->company->settings));
|
$t->replace(Ninja::transformTranslations($this->company->settings));
|
||||||
|
|
||||||
$this->$invoices = Invoice::whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->get();
|
$this->invoices = Invoice::whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->get();
|
||||||
|
|
||||||
$mail_obj = new stdClass;
|
$mail_obj = new stdClass;
|
||||||
$mail_obj->amount = $this->getAmount();
|
$mail_obj->amount = $this->getAmount();
|
||||||
|
@ -13,6 +13,7 @@ namespace App\Mail\Admin;
|
|||||||
|
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
use App\Utils\Number;
|
use App\Utils\Number;
|
||||||
|
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
use stdClass;
|
use stdClass;
|
||||||
|
|
||||||
@ -38,7 +39,11 @@ class EntityCreatedObject
|
|||||||
$this->entity = $entity;
|
$this->entity = $entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function build()
|
/**
|
||||||
|
* @return stdClass
|
||||||
|
* @throws BindingResolutionException
|
||||||
|
*/
|
||||||
|
public function build() :stdClass
|
||||||
{
|
{
|
||||||
App::forgetInstance('translator');
|
App::forgetInstance('translator');
|
||||||
/* Init a new copy of the translator*/
|
/* Init a new copy of the translator*/
|
||||||
@ -47,26 +52,64 @@ class EntityCreatedObject
|
|||||||
App::setLocale($this->entity->company->getLocale());
|
App::setLocale($this->entity->company->getLocale());
|
||||||
/* Set customized translations _NOW_ */
|
/* Set customized translations _NOW_ */
|
||||||
$t->replace(Ninja::transformTranslations($this->entity->company->settings));
|
$t->replace(Ninja::transformTranslations($this->entity->company->settings));
|
||||||
|
$this->setTemplate();
|
||||||
|
$this->company = $this->entity->company;
|
||||||
|
|
||||||
|
if($this->entity_type == 'purchase_order')
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->entity->load('vendor.company');
|
||||||
|
|
||||||
|
$mail_obj = new stdClass;
|
||||||
|
$mail_obj->amount = Number::formatMoney($this->entity->amount, $this->entity->vendor);
|
||||||
|
|
||||||
|
$mail_obj->subject = ctrans($this->template_subject,
|
||||||
|
[
|
||||||
|
'vendor' => $this->entity->vendor->present()->name(),
|
||||||
|
'purchase_order' => $this->entity->number,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$mail_obj->markdown = 'email.admin.generic';
|
||||||
|
$mail_obj->tag = $this->company->company_key;
|
||||||
|
$mail_obj->data = [
|
||||||
|
'title' => $mail_obj->subject,
|
||||||
|
'message' => ctrans($this->template_body,
|
||||||
|
[
|
||||||
|
'amount' => $mail_obj->amount,
|
||||||
|
'vendor' => $this->entity->vendor->present()->name(),
|
||||||
|
'purchase_order' => $this->entity->number,
|
||||||
|
]
|
||||||
|
),
|
||||||
|
'url' => $this->entity->invitations()->first()->getAdminLink(),
|
||||||
|
'button' => ctrans("texts.view_{$this->entity_type}"),
|
||||||
|
'signature' => $this->company->settings->email_signature,
|
||||||
|
'logo' => $this->company->present()->logo(),
|
||||||
|
'settings' => $this->company->settings,
|
||||||
|
'whitelabel' => $this->company->account->isPaid() ? true : false,
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
$this->entity->load('client.country', 'client.company');
|
$this->entity->load('client.country', 'client.company');
|
||||||
$this->client = $this->entity->client;
|
$this->client = $this->entity->client;
|
||||||
$this->company = $this->entity->company;
|
|
||||||
|
|
||||||
$this->setTemplate();
|
|
||||||
|
|
||||||
$mail_obj = new stdClass;
|
$mail_obj = new stdClass;
|
||||||
$mail_obj->amount = $this->getAmount();
|
$mail_obj->amount = $this->getAmount();
|
||||||
$mail_obj->subject = $this->getSubject();
|
$mail_obj->subject = $this->getSubject();
|
||||||
$mail_obj->data = $this->getData();
|
$mail_obj->data = $this->getData();
|
||||||
$mail_obj->markdown = 'email.admin.generic';
|
$mail_obj->markdown = 'email.admin.generic';
|
||||||
$mail_obj->tag = $this->company->company_key;
|
$mail_obj->tag = $this->entity->company->company_key;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return $mail_obj;
|
return $mail_obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function setTemplate()
|
private function setTemplate()
|
||||||
{
|
{
|
||||||
// nlog($this->template);
|
|
||||||
|
|
||||||
switch ($this->entity_type) {
|
switch ($this->entity_type) {
|
||||||
case 'invoice':
|
case 'invoice':
|
||||||
@ -81,7 +124,10 @@ class EntityCreatedObject
|
|||||||
$this->template_subject = 'texts.notification_credit_created_subject';
|
$this->template_subject = 'texts.notification_credit_created_subject';
|
||||||
$this->template_body = 'texts.notification_credit_created_body';
|
$this->template_body = 'texts.notification_credit_created_body';
|
||||||
break;
|
break;
|
||||||
|
case 'purchase_order':
|
||||||
|
$this->template_subject = 'texts.notification_purchase_order_created_subject';
|
||||||
|
$this->template_body = 'texts.notification_purchase_order_created_body';
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
$this->template_subject = 'texts.notification_invoice_created_subject';
|
$this->template_subject = 'texts.notification_invoice_created_subject';
|
||||||
$this->template_body = 'texts.notification_invoice_created_body';
|
$this->template_body = 'texts.notification_invoice_created_body';
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user