@ -26,7 +26,7 @@ BROADCAST_DRIVER=log
|
||||
LOG_CHANNEL=stack
|
||||
CACHE_DRIVER=file
|
||||
QUEUE_CONNECTION=database
|
||||
SESSION_DRIVER=cookie
|
||||
SESSION_DRIVER=file
|
||||
SESSION_LIFETIME=120
|
||||
|
||||
REDIS_HOST=127.0.0.1
|
||||
|
15
.github/workflows/phpunit.yml
vendored
@ -8,11 +8,14 @@ on:
|
||||
|
||||
name: phpunit
|
||||
jobs:
|
||||
phpunit:
|
||||
runs-on: ubuntu-latest
|
||||
run:
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
strategy:
|
||||
matrix:
|
||||
php-versions: ['7.3', '7.4', '8.0']
|
||||
operating-system: ['ubuntu-18.04', 'ubuntu-20.04']
|
||||
php-versions: ['7.3', '7.4']
|
||||
phpunit-versions: ['latest']
|
||||
|
||||
env:
|
||||
DB_DATABASE1: ninja
|
||||
DB_USERNAME1: root
|
||||
@ -60,6 +63,12 @@ jobs:
|
||||
sleep 1
|
||||
done
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
extensions: mysql, mysqlnd, sqlite3, bcmath, gmp, gd, curl, zip, openssl, mbstring, xml
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
ref: v5-develop
|
||||
|
2
.gitignore
vendored
@ -27,3 +27,5 @@ storage/migrations
|
||||
nbproject
|
||||
|
||||
.php_cs.cache
|
||||
public/test.pdf
|
||||
public/storage/test.pdf
|
||||
|
@ -2,12 +2,12 @@
|
||||
<img src="https://raw.githubusercontent.com/hillelcoren/invoice-ninja/master/public/images/round_logo.png" alt="Sublime's custom image"/>
|
||||
</p>
|
||||
|
||||

|
||||
[](https://travis-ci.org/invoiceninja/invoiceninja)
|
||||

|
||||

|
||||
[](https://codecov.io/gh/invoiceninja/invoiceninja)
|
||||
[](https://www.codacy.com/app/turbo124/invoiceninja?utm_source=github.com&utm_medium=referral&utm_content=invoiceninja/invoiceninja&utm_campaign=Badge_Grade)
|
||||
|
||||
# Invoice Ninja version 5 is coming!
|
||||
# Invoice Ninja version 5 is in Beta!
|
||||
|
||||
We will be using the lessons learnt in Invoice Ninja 4.0 to build a bigger better platform to work from. If you would like to contribute to the project we will gladly accept contributions for code, user guides, bug tracking and feedback! Please consider the following guidelines prior to submitting a pull request:
|
||||
|
||||
|
@ -1 +1 @@
|
||||
5.0.35
|
||||
5.0.36
|
@ -15,6 +15,7 @@ use App\DataMapper\CompanySettings;
|
||||
use App\Events\Invoice\InvoiceWasCreated;
|
||||
use App\Factory\InvoiceFactory;
|
||||
use App\Factory\InvoiceItemFactory;
|
||||
use App\Factory\RecurringInvoiceFactory;
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use App\Jobs\Company\CreateCompanyPaymentTerms;
|
||||
use App\Jobs\Company\CreateCompanyTaskStatuses;
|
||||
@ -30,6 +31,7 @@ use App\Models\Expense;
|
||||
use App\Models\Product;
|
||||
use App\Models\Project;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Task;
|
||||
use App\Models\User;
|
||||
use App\Models\Vendor;
|
||||
@ -238,43 +240,25 @@ class DemoMode extends Command
|
||||
$this->info('creating entities for client #'.$client->id);
|
||||
$this->createInvoice($client, $u2->id);
|
||||
|
||||
// for($y=0; $y<($this->count); $y++){
|
||||
// $this->info("creating invoice #{$y} for client #".$client->id);
|
||||
// }
|
||||
$this->createRecurringInvoice($client, $u2->id);
|
||||
|
||||
$client = $company->clients->random();
|
||||
$this->createCredit($client, $u2->id);
|
||||
|
||||
// for($y=0; $y<($this->count); $y++){
|
||||
// $this->info("creating credit #{$y} for client #".$client->id);
|
||||
// }
|
||||
|
||||
$client = $company->clients->random();
|
||||
$this->createQuote($client, $u2->id);
|
||||
|
||||
// for($y=0; $y<($this->count); $y++){
|
||||
// $this->info("creating quote #{$y} for client #".$client->id);
|
||||
// }
|
||||
|
||||
$client = $company->clients->random();
|
||||
$this->createExpense($client);
|
||||
|
||||
//$this->info("creating expense for client #".$client->id);
|
||||
|
||||
$client = $company->clients->random();
|
||||
$this->createVendor($client, $u2->id);
|
||||
|
||||
// $this->info("creating vendor for client #".$client->id);
|
||||
|
||||
$client = $company->clients->random();
|
||||
$this->createTask($client, $u2->id);
|
||||
|
||||
// $this->info("creating task for client #".$client->id);
|
||||
|
||||
$client = $company->clients->random();
|
||||
$this->createProject($client, $u2->id);
|
||||
|
||||
// $this->info("creating project for client #".$client->id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -352,6 +336,7 @@ class DemoMode extends Command
|
||||
$vendor = Task::factory()->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company_id,
|
||||
'client_id' => $client->id
|
||||
]);
|
||||
}
|
||||
|
||||
@ -431,6 +416,59 @@ class DemoMode extends Command
|
||||
event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars()));
|
||||
}
|
||||
|
||||
private function createRecurringInvoice($client, $assigned_user_id = null)
|
||||
{
|
||||
$faker = \Faker\Factory::create();
|
||||
|
||||
$invoice = RecurringInvoiceFactory::create($client->company->id, $client->user->id); //stub the company and user_id
|
||||
$invoice->client_id = $client->id;
|
||||
$invoice->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
|
||||
$invoice->last_sent_date = now()->subMonth();
|
||||
$invoice->next_send_date = now()->addMonthNoOverflow();
|
||||
|
||||
if ((bool) rand(0, 1)) {
|
||||
$dateable = Carbon::now()->subDays(rand(0, 90));
|
||||
} else {
|
||||
$dateable = Carbon::now()->addDays(rand(0, 90));
|
||||
}
|
||||
|
||||
$invoice->date = $dateable;
|
||||
|
||||
$invoice->line_items = $this->buildLineItems(rand(1, 10));
|
||||
$invoice->uses_inclusive_taxes = false;
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$invoice->tax_name1 = 'GST';
|
||||
$invoice->tax_rate1 = 10.00;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$invoice->tax_name2 = 'VAT';
|
||||
$invoice->tax_rate2 = 17.50;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$invoice->tax_name3 = 'CA Sales Tax';
|
||||
$invoice->tax_rate3 = 5;
|
||||
}
|
||||
|
||||
// $invoice->custom_value1 = $faker->date;
|
||||
// $invoice->custom_value2 = rand(0, 1) ? 'yes' : 'no';
|
||||
|
||||
$invoice->save();
|
||||
|
||||
$invoice_calc = new InvoiceSum($invoice);
|
||||
$invoice_calc->build();
|
||||
|
||||
$invoice = $invoice_calc->getInvoice();
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$invoice->assigned_user_id = $assigned_user_id;
|
||||
}
|
||||
|
||||
$invoice->save();
|
||||
}
|
||||
|
||||
private function createCredit($client, $assigned_user_id = null)
|
||||
{
|
||||
$faker = \Faker\Factory::create();
|
||||
|
@ -12,10 +12,8 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Jobs\Util\VersionCheck;
|
||||
use Composer\Console\Application;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
|
||||
class PostUpdate extends Command
|
||||
{
|
||||
@ -52,23 +50,35 @@ class PostUpdate extends Command
|
||||
info("I wasn't able to migrate the data.");
|
||||
}
|
||||
|
||||
info("finished migrating");
|
||||
|
||||
exec('vendor/bin/composer install --no-dev:');
|
||||
|
||||
info("finished running composer install ");
|
||||
|
||||
|
||||
try {
|
||||
Artisan::call('optimize');
|
||||
} catch (\Exception $e) {
|
||||
info("I wasn't able to optimize.");
|
||||
}
|
||||
|
||||
info("optimized");
|
||||
|
||||
try {
|
||||
Artisan::call('view:clear');
|
||||
} catch (\Exception $e) {
|
||||
info("I wasn't able to clear the views.");
|
||||
}
|
||||
|
||||
info("view cleared");
|
||||
|
||||
/* For the following to work, the web user (www-data) must own all the directories */
|
||||
|
||||
putenv('COMPOSER_HOME=' . __DIR__ . '/vendor/bin/composer');
|
||||
|
||||
$input = new ArrayInput(['command' => 'install', '--no-dev' => 'true']);
|
||||
$application = new Application();
|
||||
$application->setAutoExit(false);
|
||||
$application->run($input);
|
||||
|
||||
VersionCheck::dispatch();
|
||||
|
||||
echo "Done.";
|
||||
info("sent for version check");
|
||||
|
||||
// echo "Done.";
|
||||
}
|
||||
}
|
||||
|
@ -173,7 +173,6 @@ class EmailTemplateDefaults
|
||||
|
||||
public static function emailReminder1Subject()
|
||||
{
|
||||
info("reminder 1 subject");
|
||||
return ctrans('texts.reminder_subject', ['invoice'=>'$invoice.number', 'account'=>'$company.name']);
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Events\Product;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\Product;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use App\Models\Account;
|
||||
use Exception;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
@ -72,10 +71,6 @@ class Handler extends ExceptionHandler
|
||||
info('account table not found');
|
||||
return;
|
||||
}
|
||||
// if(Account::count() == 0){
|
||||
// info("handler - account table not found");
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (app()->bound('sentry') && $this->shouldReport($exception)) {
|
||||
app('sentry')->configureScope(function (Scope $scope): void {
|
||||
@ -94,13 +89,31 @@ class Handler extends ExceptionHandler
|
||||
}
|
||||
});
|
||||
|
||||
// app('sentry')->setRelease(config('ninja.app_version'));
|
||||
app('sentry')->captureException($exception);
|
||||
if ($this->validException($exception)) {
|
||||
app('sentry')->captureException($exception);
|
||||
}
|
||||
}
|
||||
|
||||
parent::report($exception);
|
||||
}
|
||||
|
||||
private function validException($exception)
|
||||
{
|
||||
if (strpos($exception->getMessage(), 'file_put_contents') !== false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strpos($exception->getMessage(), 'Permission denied') !== false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strpos($exception->getMessage(), 'flock()') !== false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
|
38
app/Export/CSV/InvoiceExport.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Export\CSV;
|
||||
|
||||
use App\Models\Company;
|
||||
use Excel;
|
||||
|
||||
class InvoiceExport
|
||||
{
|
||||
private $company;
|
||||
|
||||
public function __construct(Company $company)
|
||||
{
|
||||
$this->company = $company;
|
||||
}
|
||||
|
||||
public function export()
|
||||
{
|
||||
// $fileName = 'test.csv';
|
||||
|
||||
// $data = $this->company->invoices->get();
|
||||
|
||||
// return Excel::create($fileName, function ($excel) use ($data) {
|
||||
// $excel->sheet('', function ($sheet) use ($data) {
|
||||
// $sheet->loadView('export', $data);
|
||||
// });
|
||||
// })->download('csv');
|
||||
}
|
||||
}
|
@ -22,6 +22,18 @@ use Illuminate\Support\Facades\Gate;
|
||||
*/
|
||||
class ClientFilters extends QueryFilters
|
||||
{
|
||||
|
||||
/**
|
||||
* Filter by name.
|
||||
*
|
||||
* @param string $name
|
||||
* @return Builder
|
||||
*/
|
||||
public function name(string $name): Builder
|
||||
{
|
||||
return $this->builder->where('name', 'like', '%'.$name.'%');
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter by balance.
|
||||
*
|
||||
@ -190,7 +202,6 @@ class ClientFilters extends QueryFilters
|
||||
* limit the user to only the invoices they have created
|
||||
*/
|
||||
if (Gate::denies('view-list', Client::class)) {
|
||||
info('the gate!');
|
||||
$query->where('clients.user_id', '=', $user->id);
|
||||
}
|
||||
|
||||
|
@ -104,11 +104,11 @@ abstract class QueryFilters
|
||||
* @param string $value
|
||||
* @return stdClass
|
||||
*/
|
||||
public function split($value) : stdClass
|
||||
public function split($value) : \stdClass
|
||||
{
|
||||
$exploded_array = explode(':', $value);
|
||||
|
||||
$parts = new stdClass;
|
||||
$parts = new \stdClass;
|
||||
|
||||
$parts->value = $exploded_array[0];
|
||||
$parts->operator = $this->operatorConvertor($exploded_array[1]);
|
||||
|
@ -53,6 +53,13 @@ class DocumentController extends Controller
|
||||
return Storage::disk($document->disk)->download($document->url, $document->name);
|
||||
}
|
||||
|
||||
public function publicDownload(string $document_hash)
|
||||
{
|
||||
$document = Document::where('hash', $document_hash)->firstOrFail();
|
||||
|
||||
return Storage::disk($document->disk)->download($document->url, $document->name);
|
||||
}
|
||||
|
||||
public function downloadMultiple(DownloadMultipleDocumentsRequest $request)
|
||||
{
|
||||
$documents = Document::whereIn('id', $this->transformKeys($request->file_hash))
|
||||
|
@ -232,6 +232,7 @@ class PaymentController extends Controller
|
||||
public function response(PaymentResponseRequest $request)
|
||||
{
|
||||
$gateway = CompanyGateway::find($request->input('company_gateway_id'))->firstOrFail();
|
||||
|
||||
$payment_hash = PaymentHash::whereRaw('BINARY `hash`= ?', [$request->payment_hash])->first();
|
||||
|
||||
return $gateway
|
||||
|
@ -132,8 +132,6 @@ class EmailController extends BaseController
|
||||
}
|
||||
});
|
||||
|
||||
$entity_obj->service()->markSent()->save();
|
||||
|
||||
$entity_obj->last_sent_date = now();
|
||||
$entity_obj->save();
|
||||
|
||||
@ -145,26 +143,27 @@ class EmailController extends BaseController
|
||||
$this->entity_type = Invoice::class;
|
||||
$this->entity_transformer = InvoiceTransformer::class;
|
||||
|
||||
if($entity_obj->invitations->count() >= 1)
|
||||
if ($entity_obj->invitations->count() >= 1) {
|
||||
$entity_obj->entityEmailEvent($entity_obj->invitations->first(), 'invoice');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity_obj instanceof Quote) {
|
||||
$this->entity_type = Quote::class;
|
||||
$this->entity_transformer = QuoteTransformer::class;
|
||||
|
||||
if($entity_obj->invitations->count() >= 1)
|
||||
if ($entity_obj->invitations->count() >= 1) {
|
||||
event(new QuoteWasEmailed($entity_obj->invitations->first(), $entity_obj->company, Ninja::eventVars()));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity_obj instanceof Credit) {
|
||||
$this->entity_type = Credit::class;
|
||||
$this->entity_transformer = CreditTransformer::class;
|
||||
|
||||
if($entity_obj->invitations->count() >= 1)
|
||||
if ($entity_obj->invitations->count() >= 1) {
|
||||
event(new CreditWasEmailed($entity_obj->invitations->first(), $entity_obj->company, Ninja::eventVars()));
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity_obj instanceof RecurringInvoice) {
|
||||
@ -172,8 +171,6 @@ class EmailController extends BaseController
|
||||
$this->entity_transformer = RecurringInvoiceTransformer::class;
|
||||
}
|
||||
|
||||
$entity_obj->service()->markSent()->save();
|
||||
|
||||
return $this->itemResponse($entity_obj);
|
||||
return $this->itemResponse($entity_obj->fresh());
|
||||
}
|
||||
}
|
||||
|
133
app/Http/Controllers/ImportController.php
Normal file
@ -0,0 +1,133 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\Import\ImportRequest;
|
||||
use App\Http\Requests\Import\PreImportRequest;
|
||||
use App\Jobs\Import\CSVImport;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
use League\Csv\Reader;
|
||||
use League\Csv\Statement;
|
||||
|
||||
class ImportController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param StoreImportRequest $request
|
||||
* @return Response
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/preimport",
|
||||
* operationId="preimport",
|
||||
* tags={"imports"},
|
||||
* summary="Pre Import checks - returns a reference to the job and the headers of the CSV",
|
||||
* description="Pre Import checks - returns a reference to the job and the headers of the CSV",
|
||||
* @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\RequestBody(
|
||||
* description="The CSV file",
|
||||
* required=true,
|
||||
* @OA\MediaType(
|
||||
* mediaType="multipart/form-data",
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="binary"
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns a reference to the file",
|
||||
* @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 preimport(PreImportRequest $request)
|
||||
{
|
||||
//create a reference
|
||||
$hash = Str::random(32);
|
||||
|
||||
//store the csv in cache with an expiry of 10 minutes
|
||||
Cache::put($hash, base64_encode(file_get_contents($request->file('file')->getPathname())), 3600);
|
||||
|
||||
//parse CSV
|
||||
$csv_array = $this->getCsvData(file_get_contents($request->file('file')->getPathname()));
|
||||
|
||||
$class_map = $this->getEntityMap($request->input('entity_type'));
|
||||
|
||||
$data = [
|
||||
'hash' => $hash,
|
||||
'available' => $class_map::importable(),
|
||||
'headers' => array_slice($csv_array, 0, 2)
|
||||
];
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
public function import(ImportRequest $request)
|
||||
{
|
||||
CSVImport::dispatch($request->all(), auth()->user()->company());
|
||||
|
||||
return response()->json(['message' => 'Importing data, email will be sent on completion'], 200);
|
||||
}
|
||||
|
||||
private function getEntityMap($entity_type)
|
||||
{
|
||||
return sprintf('App\\Import\\Definitions\%sMap', ucfirst($entity_type));
|
||||
}
|
||||
|
||||
private function getCsvData($csvfile)
|
||||
{
|
||||
if (! ini_get('auto_detect_line_endings')) {
|
||||
ini_set('auto_detect_line_endings', '1');
|
||||
}
|
||||
|
||||
$csv = Reader::createFromString($csvfile);
|
||||
$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) {
|
||||
$firstCell = $headers[0];
|
||||
|
||||
if (strstr($firstCell, (string)config('ninja.app_name'))) {
|
||||
array_shift($data); // Invoice Ninja...
|
||||
array_shift($data); // <blank line>
|
||||
array_shift($data); // Enitty Type Header
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
@ -12,9 +12,7 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Events\Invoice\InvoiceReminderWasEmailed;
|
||||
use App\Events\Invoice\InvoiceWasCreated;
|
||||
use App\Events\Invoice\InvoiceWasEmailed;
|
||||
use App\Events\Invoice\InvoiceWasUpdated;
|
||||
use App\Factory\CloneInvoiceFactory;
|
||||
use App\Factory\CloneInvoiceToQuoteFactory;
|
||||
@ -31,7 +29,6 @@ use App\Jobs\Entity\EmailEntity;
|
||||
use App\Jobs\Invoice\StoreInvoice;
|
||||
use App\Jobs\Invoice\ZipInvoices;
|
||||
use App\Jobs\Util\UnlinkFile;
|
||||
use App\Models\Activity;
|
||||
use App\Models\Client;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Quote;
|
||||
@ -730,8 +727,9 @@ class InvoiceController extends BaseController
|
||||
EmailEntity::dispatch($invitation, $invoice->company, $this->reminder_template);
|
||||
});
|
||||
|
||||
if($invoice->invitations->count() >= 1)
|
||||
if ($invoice->invitations->count() >= 1) {
|
||||
$invoice->entityEmailEvent($invoice->invitations->first(), $this->reminder_template);
|
||||
}
|
||||
|
||||
if (! $bulk) {
|
||||
return response()->json(['message' => 'email sent'], 200);
|
||||
|
@ -21,10 +21,13 @@ class PaymentWebhookController extends Controller
|
||||
$this->middleware('guest');
|
||||
}
|
||||
|
||||
public function __invoke(PaymentWebhookRequest $request, string $gateway_key, string $company_key)
|
||||
public function __invoke(PaymentWebhookRequest $request, string $company_key, string $company_gateway_id)
|
||||
{
|
||||
$payment = $request->getPayment();
|
||||
$client = is_null($payment) ? $request->getClient() : $payment->client;
|
||||
|
||||
return $request->getCompanyGateway()
|
||||
->driver($request->getClient())
|
||||
->processWebhookRequest($request, $request->getPayment());
|
||||
->driver($client)
|
||||
->processWebhookRequest($request, $payment);
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Designs\Designer;
|
||||
use App\Jobs\Util\PreviewPdf;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
@ -20,10 +19,13 @@ use App\Models\InvoiceInvitation;
|
||||
use App\Services\PdfMaker\Design;
|
||||
use App\Services\PdfMaker\PdfMaker;
|
||||
use App\Utils\HtmlEngine;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\PhantomJS\Phantom;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\MakesInvoiceHtml;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Lang;
|
||||
use Illuminate\Support\Facades\Response;
|
||||
|
||||
class PreviewController extends BaseController
|
||||
@ -96,15 +98,16 @@ class PreviewController extends BaseController
|
||||
|
||||
$entity_obj->load('client');
|
||||
|
||||
App::setLocale($entity_obj->client->primary_contact()->preferredLocale());
|
||||
App::forgetInstance('translator');
|
||||
Lang::replace(Ninja::transformTranslations($entity_obj->client->getMergedSettings()));
|
||||
|
||||
$html = new HtmlEngine($entity_obj->invitations()->first());
|
||||
|
||||
$design_namespace = 'App\Services\PdfMaker\Designs\\'.request()->design['name'];
|
||||
|
||||
$design_class = new $design_namespace();
|
||||
|
||||
// $designer = new Designer($entity_obj, $design_object, $entity_obj->client->getSetting('pdf_variables'), lcfirst($entity));
|
||||
// $html = $this->generateEntityHtml($designer, $entity_obj);
|
||||
|
||||
$state = [
|
||||
'template' => $design_class->elements([
|
||||
'client' => $entity_obj->client,
|
||||
@ -122,6 +125,10 @@ class PreviewController extends BaseController
|
||||
->design($design)
|
||||
->build();
|
||||
|
||||
if (request()->has('html') && request()->input('html') == true) {
|
||||
return $maker->getCompiledHTML;
|
||||
}
|
||||
|
||||
//if phantom js...... inject here..
|
||||
if (config('ninja.phantomjs_pdf_generation')) {
|
||||
return (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true));
|
||||
@ -138,6 +145,9 @@ class PreviewController extends BaseController
|
||||
|
||||
private function blankEntity()
|
||||
{
|
||||
App::forgetInstance('translator');
|
||||
Lang::replace(Ninja::transformTranslations(auth()->user()->company()->settings));
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
$client = Client::factory()->create([
|
||||
|
@ -73,9 +73,10 @@ class SelfUpdateController extends BaseController
|
||||
info($e->getMessage());
|
||||
return response()->json(['message'=>$e->getMessage()], 500);
|
||||
}
|
||||
info('Are there any changes to pull? '.$repo->hasChanges());
|
||||
|
||||
Artisan::call('ninja:post-update');
|
||||
dispatch(function () {
|
||||
Artisan::call('ninja:post-update');
|
||||
});
|
||||
|
||||
return response()->json(['message' => ''], 200);
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ use App\Models\Account;
|
||||
use App\Utils\CurlUtils;
|
||||
use App\Utils\SystemHealth;
|
||||
use App\Utils\Traits\AppSetup;
|
||||
use Beganovich\ChromiumPdf\ChromiumPdf;
|
||||
use DB;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
@ -97,14 +98,8 @@ class SetupController extends Controller
|
||||
|
||||
$mail_driver = $request->input('mail_driver');
|
||||
|
||||
$url = $request->input('url');
|
||||
|
||||
if (substr($url, -1) != '/') {
|
||||
$url = $url . '/';
|
||||
}
|
||||
|
||||
$env_values = [
|
||||
'APP_URL' => $url,
|
||||
'APP_URL' => $request->input('url'),
|
||||
'REQUIRE_HTTPS' => $request->input('https') ? 'true' : 'false',
|
||||
'APP_DEBUG' => $request->input('debug') ? 'true' : 'false',
|
||||
|
||||
@ -164,12 +159,12 @@ class SetupController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Return status based on check of database connection.
|
||||
* Return status based on database check.
|
||||
*
|
||||
* @param CheckDatabaseRequest $request
|
||||
* @return Response
|
||||
* @return Application|ResponseFactory|JsonResponse|Response
|
||||
*/
|
||||
public function checkDB(CheckDatabaseRequest $request): Response
|
||||
public function checkDB(CheckDatabaseRequest $request)
|
||||
{
|
||||
try {
|
||||
$status = SystemHealth::dbCheck($request);
|
||||
@ -227,15 +222,26 @@ class SetupController extends Controller
|
||||
return $this->testPhantom();
|
||||
}
|
||||
|
||||
Browsershot::html('GENERATING PDFs WORKS! Thank you for using Invoice Ninja!')
|
||||
->setNodeBinary(config('ninja.system.node_path'))
|
||||
->setNpmBinary(config('ninja.system.npm_path'))
|
||||
->noSandbox()
|
||||
->savePdf(
|
||||
public_path('test.pdf')
|
||||
);
|
||||
if (config('ninja.experimental_pdf_engine')) {
|
||||
$chromium_pdf = new ChromiumPdf();
|
||||
|
||||
return response(['url' => asset('test.pdf')], 200);
|
||||
$pdf = $chromium_pdf
|
||||
->setChromiumPath(config('ninja.experimental_pdf_engine_chromium_path'))
|
||||
->setHtml('GENERATING PDFs WORKS! Thank you for using Invoice Ninja!')
|
||||
->generate();
|
||||
|
||||
Storage::put('public/test.pdf', $pdf);
|
||||
} else {
|
||||
Browsershot::html('GENERATING PDFs WORKS! Thank you for using Invoice Ninja!')
|
||||
->setNodeBinary(config('ninja.system.node_path'))
|
||||
->setNpmBinary(config('ninja.system.npm_path'))
|
||||
->noSandbox()
|
||||
->savePdf(
|
||||
public_path('storage/test.pdf')
|
||||
);
|
||||
}
|
||||
|
||||
return response(['url' => asset('storage/test.pdf')], 200);
|
||||
} catch (Exception $e) {
|
||||
info($e->getMessage());
|
||||
|
||||
|
@ -35,9 +35,12 @@ class StoreExpenseRequest extends Request
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
$rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id);
|
||||
if (isset($this->number)) {
|
||||
$rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id);
|
||||
}
|
||||
|
||||
// $rules['number'] = 'unique:expenses,number,'.$this->id.',id,company_id,'.auth()->user()->company()->id;
|
||||
$rules['contacts.*.email'] = 'nullable|distinct';
|
||||
// $rules['contacts.*.email'] = 'nullable|distinct';
|
||||
//$rules['number'] = new UniqueExpenseNumberRule($this->all());
|
||||
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||
|
||||
@ -55,6 +58,10 @@ class StoreExpenseRequest extends Request
|
||||
$input['category_id'] = $this->decodePrimaryKey($input['category_id']);
|
||||
}
|
||||
|
||||
if (! array_key_exists('currency_id', $input) || strlen($input['currency_id']) == 0) {
|
||||
$input['currency_id'] = (string)auth()->user()->company()->settings->currency_id;
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
@ -62,8 +69,6 @@ class StoreExpenseRequest extends Request
|
||||
{
|
||||
return [
|
||||
'unique' => ctrans('validation.unique', ['attribute' => 'email']),
|
||||
//'required' => trans('validation.required', ['attribute' => 'email']),
|
||||
'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -66,6 +66,10 @@ class UpdateExpenseRequest extends Request
|
||||
$input['category_id'] = $this->decodePrimaryKey($input['category_id']);
|
||||
}
|
||||
|
||||
if (! array_key_exists('currency_id', $input) || strlen($input['currency_id']) == 0) {
|
||||
$input['currency_id'] = (string)auth()->user()->company()->settings->currency_id;
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
}
|
||||
|
37
app/Http/Requests/Import/ImportRequest.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Import;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
|
||||
class ImportRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'hash' => 'required|string',
|
||||
'entity_type' => 'required|string',
|
||||
'column_map' => 'required|array',
|
||||
'skip_header' => 'required|boolean'
|
||||
];
|
||||
}
|
||||
}
|
35
app/Http/Requests/Import/PreImportRequest.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Import;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
|
||||
class PreImportRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'file' => 'required|file|mimes:csv,txt',
|
||||
'entity_type' => 'required',
|
||||
];
|
||||
}
|
||||
}
|
@ -49,6 +49,7 @@ class UpdateInvoiceRequest extends Request
|
||||
|
||||
$rules['id'] = new LockedInvoiceRule($this->invoice);
|
||||
|
||||
// if ($this->input('number') && strlen($this->input('number')) >= 1) {
|
||||
if ($this->input('number')) {
|
||||
$rules['number'] = 'unique:invoices,number,'.$this->id.',id,company_id,'.$this->invoice->company_id;
|
||||
}
|
||||
|
@ -18,9 +18,12 @@ use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class PaymentWebhookRequest extends Request
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
@ -41,20 +44,22 @@ class PaymentWebhookRequest extends Request
|
||||
*/
|
||||
public function getCompanyGateway(): ?CompanyGateway
|
||||
{
|
||||
return CompanyGateway::where('gateway_key', $this->gateway_key)->firstOrFail();
|
||||
return CompanyGateway::find($this->decodePrimaryKey($this->company_gateway_id))->firstOrFail();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve payment hash.
|
||||
*
|
||||
* @param string $hash
|
||||
* @return null|\App\Http\Requests\Payments\PaymentHash
|
||||
* @return null|\App\Models\PaymentHash
|
||||
*/
|
||||
public function getPaymentHash(): ?PaymentHash
|
||||
{
|
||||
if ($this->query('hash')) {
|
||||
return PaymentHash::where('hash', $this->query('hash'))->firstOrFail();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -64,9 +69,15 @@ class PaymentWebhookRequest extends Request
|
||||
*/
|
||||
public function getPayment(): ?Payment
|
||||
{
|
||||
$hash = $this->getPaymentHash();
|
||||
/**
|
||||
* Some gateways, like Checkout, we can dynamically pass payment hash,
|
||||
* which we will resolve here and get payment information from it.
|
||||
*/
|
||||
if ($this->getPaymentHash()) {
|
||||
return $this->getPaymentHash()->payment;
|
||||
}
|
||||
|
||||
return $hash->payment;
|
||||
abort(404);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -15,6 +15,7 @@ use App\Http\Requests\Request;
|
||||
use App\Models\Client;
|
||||
use App\Models\Project;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class StoreProjectRequest extends Request
|
||||
{
|
||||
@ -36,7 +37,10 @@ class StoreProjectRequest extends Request
|
||||
|
||||
$rules['name'] = 'required';
|
||||
$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||
$rules['number'] = 'unique:projects,number,'.$this->id.',id,company_id,'.auth()->user()->company()->id;
|
||||
|
||||
if (isset($this->number)) {
|
||||
$rules['number'] = Rule::unique('projects')->where('company_id', auth()->user()->company()->id);
|
||||
}
|
||||
|
||||
return $this->globalRules($rules);
|
||||
}
|
||||
|
@ -113,11 +113,12 @@ class StoreRecurringInvoiceRequest extends Request
|
||||
|
||||
private function setAutoBillFlag($auto_bill)
|
||||
{
|
||||
if ($auto_bill == 'always')
|
||||
if ($auto_bill == 'always') {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//if ($auto_bill == 'off' || $auto_bill == 'optin') {
|
||||
//if ($auto_bill == 'off' || $auto_bill == 'optin') {
|
||||
return false;
|
||||
//}
|
||||
}
|
||||
|
@ -38,10 +38,11 @@ class StoreUserRequest extends Request
|
||||
$rules['first_name'] = 'required|string|max:100';
|
||||
$rules['last_name'] = 'required|string|max:100';
|
||||
|
||||
if (config('ninja.db.multi_db_enabled'))
|
||||
if (config('ninja.db.multi_db_enabled')) {
|
||||
$rules['email'] = [new ValidUserForCompany(), Rule::unique('users')];
|
||||
else
|
||||
} else {
|
||||
$rules['email'] = Rule::unique('users');
|
||||
}
|
||||
|
||||
|
||||
if (auth()->user()->company()->account->isFreeHostedClient()) {
|
||||
|
101
app/Import/Definitions/ClientMap.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/**
|
||||
* client Ninja (https://clientninja.com).
|
||||
*
|
||||
* @link https://github.com/clientninja/clientninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. client Ninja LLC (https://clientninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Import\Definitions;
|
||||
|
||||
class ClientMap
|
||||
{
|
||||
public static function importable()
|
||||
{
|
||||
return [
|
||||
0 => 'client.name',
|
||||
1 => 'client.user_id',
|
||||
2 => 'client.balance',
|
||||
3 => 'client.paid_to_date',
|
||||
4 => 'client.currency_id',
|
||||
5 => 'client.website',
|
||||
6 => 'client.private_notes',
|
||||
7 => 'client.industry_id',
|
||||
8 => 'client.size_id',
|
||||
9 => 'client.address1',
|
||||
10 => 'client.address2',
|
||||
11 => 'client.city',
|
||||
12 => 'client.state',
|
||||
13 => 'client.postal_code',
|
||||
14 => 'client.country_id',
|
||||
15 => 'client.custom_value1',
|
||||
16 => 'client.custom_value2',
|
||||
17 => 'client.custom_value3',
|
||||
18 => 'client.custom_value4',
|
||||
19 => 'client.shipping_address1',
|
||||
20 => 'client.shipping_address2',
|
||||
21 => 'client.shipping_city',
|
||||
22 => 'client.shipping_state',
|
||||
23 => 'client.shipping_postal_code',
|
||||
24 => 'client.shipping_country_id',
|
||||
25 => 'client.payment_terms',
|
||||
26 => 'client.vat_number',
|
||||
27 => 'client.id_number',
|
||||
28 => 'client.public_notes',
|
||||
29 => 'contact.first_name',
|
||||
30 => 'contact.last_name',
|
||||
31 => 'contact.email',
|
||||
32 => 'contact.phone',
|
||||
33 => 'contact.custom_value1',
|
||||
34 => 'contact.custom_value2',
|
||||
35 => 'contact.custom_value3',
|
||||
36 => 'contact.custom_value4',
|
||||
];
|
||||
}
|
||||
|
||||
public static function import_keys()
|
||||
{
|
||||
return [
|
||||
0 => 'texts.client_name',
|
||||
1 => 'texts.user',
|
||||
2 => 'texts.balance',
|
||||
3 => 'texts.paid_to_date',
|
||||
4 => 'texts.currency',
|
||||
5 => 'texts.website',
|
||||
6 => 'texts.private_notes',
|
||||
7 => 'texts.industry',
|
||||
8 => 'texts.size',
|
||||
9 => 'texts.address1',
|
||||
10 => 'texts.address2',
|
||||
11 => 'texts.city',
|
||||
12 => 'texts.state',
|
||||
13 => 'texts.postal_code',
|
||||
14 => 'texts.country',
|
||||
15 => 'texts.custom_value',
|
||||
16 => 'texts.custom_value',
|
||||
17 => 'texts.custom_value',
|
||||
18 => 'texts.custom_value',
|
||||
19 => 'texts.address1',
|
||||
20 => 'texts.address2',
|
||||
21 => 'texts.shipping_city',
|
||||
22 => 'texts.shipping_state',
|
||||
23 => 'texts.shipping_postal_code',
|
||||
24 => 'texts.shipping_country',
|
||||
25 => 'texts.payment_terms',
|
||||
26 => 'texts.vat_number',
|
||||
27 => 'texts.id_number',
|
||||
28 => 'texts.public_notes',
|
||||
29 => 'texts.first_name',
|
||||
30 => 'texts.last_name',
|
||||
31 => 'texts.email',
|
||||
32 => 'texts.phone',
|
||||
33 => 'texts.custom_value',
|
||||
34 => 'texts.custom_value',
|
||||
35 => 'texts.custom_value',
|
||||
36 => 'texts.custom_value',
|
||||
];
|
||||
}
|
||||
}
|
133
app/Import/Definitions/InvoiceMap.php
Normal file
@ -0,0 +1,133 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Import\Definitions;
|
||||
|
||||
class InvoiceMap
|
||||
{
|
||||
public static function importable()
|
||||
{
|
||||
return [
|
||||
0 => 'invoice.number',
|
||||
1 => 'invoice.user_id',
|
||||
2 => 'invoice.amount',
|
||||
3 => 'invoice.balance',
|
||||
4 => 'invoice.client_id',
|
||||
5 => 'invoice.discount',
|
||||
6 => 'invoice.po_number',
|
||||
7 => 'invoice.date',
|
||||
8 => 'invoice.due_date',
|
||||
9 => 'invoice.terms',
|
||||
10 => 'invoice.public_notes',
|
||||
11 => 'invoice.is_sent',
|
||||
12 => 'invoice.private_notes',
|
||||
13 => 'invoice.uses_inclusive_taxes',
|
||||
14 => 'invoice.tax_name1',
|
||||
15 => 'invoice.tax_rate1',
|
||||
16 => 'invoice.tax_name2',
|
||||
17 => 'invoice.tax_rate2',
|
||||
18 => 'invoice.tax_name3',
|
||||
19 => 'invoice.tax_rate3',
|
||||
20 => 'invoice.is_amount_discount',
|
||||
21 => 'invoice.footer',
|
||||
22 => 'invoice.partial',
|
||||
23 => 'invoice.partial_due_date',
|
||||
24 => 'invoice.custom_value1',
|
||||
25 => 'invoice.custom_value2',
|
||||
26 => 'invoice.custom_value3',
|
||||
27 => 'invoice.custom_value4',
|
||||
28 => 'invoice.custom_surcharge1',
|
||||
29 => 'invoice.custom_surcharge2',
|
||||
30 => 'invoice.custom_surcharge3',
|
||||
31 => 'invoice.custom_surcharge4',
|
||||
32 => 'invoice.exchange_rate',
|
||||
33 => 'payment.date',
|
||||
34 => 'payment.amount',
|
||||
35 => 'payment.transaction_reference',
|
||||
36 => 'item.quantity',
|
||||
37 => 'item.cost',
|
||||
38 => 'item.product_key',
|
||||
39 => 'item.notes',
|
||||
40 => 'item.discount',
|
||||
41 => 'item.is_amount_discount',
|
||||
42 => 'item.tax_name1',
|
||||
43 => 'item.tax_rate1',
|
||||
44 => 'item.tax_name2',
|
||||
45 => 'item.tax_rate2',
|
||||
46 => 'item.tax_name3',
|
||||
47 => 'item.tax_rate3',
|
||||
48 => 'item.custom_value1',
|
||||
49 => 'item.custom_value2',
|
||||
50 => 'item.custom_value3',
|
||||
51 => 'item.custom_value4',
|
||||
52 => 'item.type_id',
|
||||
];
|
||||
}
|
||||
|
||||
public static function import_keys()
|
||||
{
|
||||
return [
|
||||
0 => 'texts.invoice_number',
|
||||
1 => 'texts.user',
|
||||
2 => 'texts.amount',
|
||||
3 => 'texts.balance',
|
||||
4 => 'texts.client',
|
||||
5 => 'texts.discount',
|
||||
6 => 'texts.po_number',
|
||||
7 => 'texts.date',
|
||||
8 => 'texts.due_date',
|
||||
9 => 'texts.terms',
|
||||
10 => 'texts.public_notes',
|
||||
11 => 'texts.sent',
|
||||
12 => 'texts.private_notes',
|
||||
13 => 'texts.uses_inclusive_taxes',
|
||||
14 => 'texts.tax_name',
|
||||
15 => 'texts.tax_rate',
|
||||
16 => 'texts.tax_name',
|
||||
17 => 'texts.tax_rate',
|
||||
18 => 'texts.tax_name',
|
||||
19 => 'texts.tax_rate',
|
||||
20 => 'texts.is_amount_discount',
|
||||
21 => 'texts.footer',
|
||||
22 => 'texts.partial',
|
||||
23 => 'texts.partial_due_date',
|
||||
24 => 'texts.custom_value1',
|
||||
25 => 'texts.custom_value2',
|
||||
26 => 'texts.custom_value3',
|
||||
27 => 'texts.custom_value4',
|
||||
28 => 'texts.surcharge',
|
||||
29 => 'texts.surcharge',
|
||||
30 => 'texts.surcharge',
|
||||
31 => 'texts.surcharge',
|
||||
32 => 'texts.exchange_rate',
|
||||
33 => 'texts.payment_date',
|
||||
34 => 'texts.payment_amount',
|
||||
35 => 'texts.transaction_reference',
|
||||
36 => 'texts.quantity',
|
||||
37 => 'texts.cost',
|
||||
38 => 'texts.product_key',
|
||||
39 => 'texts.notes',
|
||||
40 => 'texts.discount',
|
||||
41 => 'texts.is_amount_discount',
|
||||
42 => 'texts.tax_name',
|
||||
43 => 'texts.tax_rate',
|
||||
44 => 'texts.tax_name',
|
||||
45 => 'texts.tax_rate',
|
||||
46 => 'texts.tax_name',
|
||||
47 => 'texts.tax_rate',
|
||||
48 => 'texts.custom_value',
|
||||
49 => 'texts.custom_value',
|
||||
50 => 'texts.custom_value',
|
||||
51 => 'texts.custom_value',
|
||||
52 => 'texts.type',
|
||||
];
|
||||
}
|
||||
}
|
59
app/Import/Definitions/ProductMap.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* client Ninja (https://clientninja.com).
|
||||
*
|
||||
* @link https://github.com/clientninja/clientninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. client Ninja LLC (https://clientninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Import\Definitions;
|
||||
|
||||
class ProductMap
|
||||
{
|
||||
public static function importable()
|
||||
{
|
||||
return [
|
||||
0 => 'product.product_key',
|
||||
1 => 'product.notes',
|
||||
2 => 'product.cost',
|
||||
3 => 'product.price',
|
||||
4 => 'product.quantity',
|
||||
5 => 'product.tax_name1',
|
||||
6 => 'product.tax_rate1',
|
||||
7 => 'product.tax_name2',
|
||||
8 => 'product.tax_rate2',
|
||||
9 => 'product.tax_name3',
|
||||
10 => 'product.tax_rate3',
|
||||
11 => 'product.custom_value1',
|
||||
12 => 'product.custom_value2',
|
||||
13 => 'product.custom_value3',
|
||||
14 => 'product.custom_value4',
|
||||
15 => 'product.user_id',
|
||||
];
|
||||
}
|
||||
|
||||
public static function import_keys()
|
||||
{
|
||||
return [
|
||||
0 => 'texts.item',
|
||||
1 => 'texts.notes',
|
||||
2 => 'texts.cost',
|
||||
3 => 'texts.price',
|
||||
4 => 'texts.quantity',
|
||||
5 => 'texts.tax_name',
|
||||
6 => 'texts.tax_rate',
|
||||
7 => 'texts.tax_name',
|
||||
8 => 'texts.tax_rate',
|
||||
9 => 'texts.tax_name',
|
||||
10 => 'texts.tax_rate',
|
||||
11 => 'texts.custom_value',
|
||||
12 => 'texts.custom_value',
|
||||
13 => 'texts.custom_value',
|
||||
14 => 'texts.custom_value',
|
||||
15 => 'texts.user',
|
||||
];
|
||||
}
|
||||
}
|
351
app/Import/Transformers/BaseTransformer.php
Normal file
@ -0,0 +1,351 @@
|
||||
<?php
|
||||
|
||||
namespace App\Import\Transformers;
|
||||
|
||||
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 getCurrencyByCode($data)
|
||||
{
|
||||
$code = array_key_exists('client.currency_id', $data) ? $data['client.currency_id'] : false;
|
||||
|
||||
if ($code) {
|
||||
return $this->maps['currencies']->where('code', $code)->first()->id;
|
||||
}
|
||||
|
||||
return $this->maps['company']->settings->currency_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasClient($name)
|
||||
{
|
||||
$name = trim(strtolower($name));
|
||||
|
||||
return isset($this->maps[ENTITY_CLIENT][$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasVendor($name)
|
||||
{
|
||||
$name = trim(strtolower($name));
|
||||
|
||||
return isset($this->maps[ENTITY_VENDOR][$name]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasProduct($key)
|
||||
{
|
||||
$key = trim(strtolower($key));
|
||||
|
||||
return isset($this->maps[ENTITY_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)
|
||||
{
|
||||
return (isset($data->$field) && $data->$field) ? Utils::parseFloat($data->$field) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getClientId($name)
|
||||
{
|
||||
$name = strtolower(trim($name));
|
||||
|
||||
return isset($this->maps[ENTITY_CLIENT][$name]) ? $this->maps[ENTITY_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));
|
||||
|
||||
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 ? str_pad(trim($number), 4, '0', STR_PAD_LEFT) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoiceNumber
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getInvoiceId($invoiceNumber)
|
||||
{
|
||||
$invoiceNumber = $this->getInvoiceNumber($invoiceNumber);
|
||||
$invoiceNumber = strtolower($invoiceNumber);
|
||||
return isset($this->maps[ENTITY_INVOICE][$invoiceNumber]) ? $this->maps[ENTITY_INVOICE][$invoiceNumber] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoiceNumber
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getInvoicePublicId($invoiceNumber)
|
||||
{
|
||||
$invoiceNumber = $this->getInvoiceNumber($invoiceNumber);
|
||||
$invoiceNumber = strtolower($invoiceNumber);
|
||||
return isset($this->maps['invoices'][$invoiceNumber]) ? $this->maps['invoices'][$invoiceNumber]->public_id : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoiceNumber
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasInvoice($invoiceNumber)
|
||||
{
|
||||
$invoiceNumber = $this->getInvoiceNumber($invoiceNumber);
|
||||
$invoiceNumber = strtolower($invoiceNumber);
|
||||
|
||||
return isset($this->maps[ENTITY_INVOICE][$invoiceNumber]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoiceNumber
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getInvoiceClientId($invoiceNumber)
|
||||
{
|
||||
$invoiceNumber = $this->getInvoiceNumber($invoiceNumber);
|
||||
$invoiceNumber = strtolower($invoiceNumber);
|
||||
|
||||
return isset($this->maps[ENTITY_INVOICE.'_'.ENTITY_CLIENT][$invoiceNumber]) ? $this->maps[ENTITY_INVOICE.'_'.ENTITY_CLIENT][$invoiceNumber] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getVendorId($name)
|
||||
{
|
||||
$name = strtolower(trim($name));
|
||||
|
||||
return isset($this->maps[ENTITY_VENDOR][$name]) ? $this->maps[ENTITY_VENDOR][$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getExpenseCategoryId($name)
|
||||
{
|
||||
$name = strtolower(trim($name));
|
||||
|
||||
return isset($this->maps[ENTITY_EXPENSE_CATEGORY][$name]) ? $this->maps[ENTITY_EXPENSE_CATEGORY][$name] : null;
|
||||
}
|
||||
}
|
69
app/Import/Transformers/ClientTransformer.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
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'),
|
||||
'work_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->getString($data, 'client.balance'),
|
||||
'paid_to_date' => $this->getString($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,
|
||||
];
|
||||
}
|
||||
}
|
38
app/Import/Transformers/ProductTransformer.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Import\Transformers;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class ProductTransformer.
|
||||
*/
|
||||
class ProductTransformer extends BaseTransformer
|
||||
{
|
||||
/**
|
||||
* @param $data
|
||||
*
|
||||
* @return bool|Item
|
||||
*/
|
||||
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->getString($data, 'product.cost'),
|
||||
'price' => $this->getString($data, 'product.price'),
|
||||
'quantity' => $this->getString($data, 'product.quantity'),
|
||||
'tax_name1' => $this->getString($data, 'product.tax_name1'),
|
||||
'tax_rate1' => $this->getString($data, 'product.tax_rate1'),
|
||||
'tax_name2' => $this->getString($data, 'product.tax_name2'),
|
||||
'tax_rate2' => $this->getString($data, 'product.tax_rate2'),
|
||||
'tax_name3' => $this->getString($data, 'product.tax_name3'),
|
||||
'tax_rate3' => $this->getString($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'),
|
||||
];
|
||||
}
|
||||
}
|
@ -87,7 +87,6 @@ class CreateEntityPdf implements ShouldQueue
|
||||
|
||||
public function handle()
|
||||
{
|
||||
|
||||
App::setLocale($this->contact->preferredLocale());
|
||||
App::forgetInstance('translator');
|
||||
Lang::replace(Ninja::transformTranslations($this->entity->client->getMergedSettings()));
|
||||
@ -158,10 +157,19 @@ class CreateEntityPdf implements ShouldQueue
|
||||
info(print_r($e->getMessage(), 1));
|
||||
}
|
||||
|
||||
if (config('ninja.log_pdf_html')) {
|
||||
info($maker->getCompiledHTML());
|
||||
}
|
||||
|
||||
if ($pdf) {
|
||||
$instance = Storage::disk($this->disk)->put($file_path, $pdf);
|
||||
}
|
||||
|
||||
return $file_path;
|
||||
}
|
||||
|
||||
public function failed(\Exception $exception)
|
||||
{
|
||||
info("help!");
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +101,6 @@ class EmailEntity extends BaseMailerJob implements ShouldQueue
|
||||
$this->setMailDriver();
|
||||
|
||||
try {
|
||||
|
||||
Mail::to($this->invitation->contact->email, $this->invitation->contact->present()->name())
|
||||
->send(
|
||||
new TemplateEmail(
|
||||
|
243
app/Jobs/Import/CSVImport.php
Normal file
@ -0,0 +1,243 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Jobs\Import;
|
||||
|
||||
use App\Factory\ClientFactory;
|
||||
use App\Factory\ProductFactory;
|
||||
use App\Http\Requests\Client\StoreClientRequest;
|
||||
use App\Http\Requests\Product\StoreProductRequest;
|
||||
use App\Import\Transformers\ClientTransformer;
|
||||
use App\Import\Transformers\ProductTransformer;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\Currency;
|
||||
use App\Models\User;
|
||||
use App\Repositories\ClientContactRepository;
|
||||
use App\Repositories\ClientRepository;
|
||||
use App\Repositories\ProductRepository;
|
||||
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\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use League\Csv\Reader;
|
||||
use League\Csv\Statement;
|
||||
|
||||
class CSVImport implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $invoice;
|
||||
|
||||
public $company;
|
||||
|
||||
public $hash;
|
||||
|
||||
public $entity_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->entity_type = $request['entity_type'];
|
||||
|
||||
$this->skip_header = $request['skip_header'];
|
||||
|
||||
$this->column_map = $request['column_map'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
$this->company->owner()->setCompany($this->company);
|
||||
Auth::login($this->company->owner(), true);
|
||||
|
||||
$this->buildMaps();
|
||||
|
||||
//sort the array by key
|
||||
ksort($this->column_map);
|
||||
|
||||
$this->{"import".ucfirst($this->entity_type)}();
|
||||
|
||||
info(print_r($this->maps,1));
|
||||
}
|
||||
|
||||
public function failed($exception)
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private function importProduct()
|
||||
{
|
||||
info("importing products");
|
||||
$product_repository = new ProductRepository();
|
||||
$product_transformer = new ProductTransformer($this->maps);
|
||||
|
||||
$records = $this->getCsvData();
|
||||
|
||||
if ($this->skip_header)
|
||||
array_shift($records);
|
||||
|
||||
foreach ($records as $record)
|
||||
{
|
||||
$keys = $this->column_map;
|
||||
$values = array_intersect_key($record, $this->column_map);
|
||||
|
||||
$product_data = array_combine($keys, $values);
|
||||
|
||||
$product = $product_transformer->transform($product_data);
|
||||
|
||||
$validator = Validator::make($product, (new StoreProductRequest())->rules());
|
||||
|
||||
if ($validator->fails()) {
|
||||
$this->error_array[] = ['product' => $product, 'error' => json_encode($validator->errors())];
|
||||
} else {
|
||||
$product = $product_repository->save($product, ProductFactory::create($this->company->id, $this->setUser($record)));
|
||||
|
||||
$product->save();
|
||||
|
||||
$this->maps['products'][] = $product->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//todo limit client imports for hosted version
|
||||
private function importClient()
|
||||
{
|
||||
//clients
|
||||
$records = $this->getCsvData();
|
||||
|
||||
$contact_repository = new ClientContactRepository();
|
||||
$client_repository = new ClientRepository($contact_repository);
|
||||
$client_transformer = new ClientTransformer($this->maps);
|
||||
|
||||
if ($this->skip_header)
|
||||
array_shift($records);
|
||||
|
||||
foreach ($records as $record) {
|
||||
|
||||
$keys = $this->column_map;
|
||||
$values = array_intersect_key($record, $this->column_map);
|
||||
|
||||
$client_data = array_combine($keys, $values);
|
||||
|
||||
$client = $client_transformer->transform($client_data);
|
||||
|
||||
$validator = Validator::make($client, (new StoreClientRequest())->rules());
|
||||
|
||||
if ($validator->fails()) {
|
||||
$this->error_array[] = ['client' => $client, 'error' => json_encode($validator->errors())];
|
||||
} else {
|
||||
$client = $client_repository->save($client, ClientFactory::create($this->company->id, $this->setUser($record)));
|
||||
|
||||
if (array_key_exists('client.balance', $client_data)) {
|
||||
$client->balance = preg_replace('/[^0-9,.]+/', '', $client_data['client.balance']);
|
||||
}
|
||||
|
||||
if (array_key_exists('client.paid_to_date', $client_data)) {
|
||||
$client->paid_to_date = preg_replace('/[^0-9,.]+/', '', $client_data['client.paid_to_date']);
|
||||
}
|
||||
|
||||
$client->save();
|
||||
|
||||
$this->maps['clients'][] = $client->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
private function buildMaps()
|
||||
{
|
||||
$this->maps['currencies'] = Currency::all();
|
||||
$this->maps['users'] = $this->company->users;
|
||||
$this->maps['company'] = $this->company;
|
||||
$this->maps['clients'] = [];
|
||||
$this->maps['products'] = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
private function setUser($record)
|
||||
{
|
||||
$user_key_exists = array_search('client.user_id', $this->column_map);
|
||||
|
||||
if ($user_key_exists) {
|
||||
return $this->findUser($record[$user_key_exists]);
|
||||
} else {
|
||||
return $this->company->owner()->id;
|
||||
}
|
||||
}
|
||||
|
||||
private function findUser($user_hash)
|
||||
{
|
||||
$user = User::where('company_id', $this->company->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()
|
||||
{
|
||||
$base64_encoded_csv = Cache::get($this->hash);
|
||||
$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) {
|
||||
$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;
|
||||
}
|
||||
}
|
@ -104,6 +104,7 @@ class Import implements ShouldQueue
|
||||
* @var array
|
||||
*/
|
||||
private $available_imports = [
|
||||
'account',
|
||||
'company',
|
||||
'users',
|
||||
'payment_terms',
|
||||
@ -225,6 +226,13 @@ class Import implements ShouldQueue
|
||||
});
|
||||
}
|
||||
|
||||
private function processAccount(array $data) :void
|
||||
{
|
||||
$account = $this->company->account;
|
||||
$account->fill($data);
|
||||
$account->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @throws Exception
|
||||
@ -1048,13 +1056,11 @@ class Import implements ShouldQueue
|
||||
|
||||
$cgt = ClientGatewayToken::Create($modified);
|
||||
|
||||
$old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;
|
||||
$key = "client_gateway_tokens_{$resource['id']}";
|
||||
|
||||
$this->ids['client_gateway_tokens'] = [
|
||||
"client_gateway_tokens_{$old_user_key}" => [
|
||||
'old' => $resource['id'],
|
||||
'new' => $cgt->id,
|
||||
],
|
||||
$this->ids['client_gateway_tokens'][$key] = [
|
||||
'old' => $resource['id'],
|
||||
'new' => $cgt->id,
|
||||
];
|
||||
}
|
||||
|
||||
@ -1079,13 +1085,11 @@ class Import implements ShouldQueue
|
||||
|
||||
$task_status = TaskStatus::Create($modified);
|
||||
|
||||
$old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;
|
||||
$key = "task_statuses_{$resource['id']}";
|
||||
|
||||
$this->ids['task_statuses'] = [
|
||||
"task_statuses_{$old_user_key}" => [
|
||||
'old' => $resource['id'],
|
||||
'new' => $task_status->id,
|
||||
],
|
||||
$this->ids['task_statuses'][$key] = [
|
||||
'old' => $resource['id'],
|
||||
'new' => $task_status->id,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -35,12 +35,6 @@ class UploadAvatar implements ShouldQueue
|
||||
|
||||
public function handle() : ?string
|
||||
{
|
||||
|
||||
//make dir
|
||||
// info("avatar dir creation => ". $this->directory);
|
||||
|
||||
// Storage::makeDirectory($this->directory, 0775);
|
||||
|
||||
$tmp_file = sha1(time()).'.png';
|
||||
|
||||
$im = imagecreatefromstring(file_get_contents($this->file));
|
||||
@ -50,8 +44,8 @@ class UploadAvatar implements ShouldQueue
|
||||
|
||||
$path = Storage::putFile($this->directory, new File(sys_get_temp_dir().'/'.$tmp_file));
|
||||
|
||||
info($path);
|
||||
info($tmp_file);
|
||||
// info($path);
|
||||
// info($tmp_file);
|
||||
|
||||
$url = Storage::url($path);
|
||||
|
||||
|
@ -13,8 +13,6 @@ namespace App\Models;
|
||||
|
||||
use App\DataMapper\ClientSettings;
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\Gateway;
|
||||
use App\Models\Presenters\ClientPresenter;
|
||||
use App\Services\Client\ClientService;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
@ -485,7 +483,7 @@ class Client extends BaseModel implements HasLocalePreference
|
||||
$payment_methods_intersect = $payment_methods_collections->intersectByKeys($payment_methods_collections->flatten(1)->unique());
|
||||
|
||||
// handle custom gateways as they are not unique'd()---------------------------------------------------------
|
||||
// we need to split the query here as we allow multiple custom gateways, so we must show all of them, they query logic
|
||||
// we need to split the query here as we allow multiple custom gateways, so we must show all of them, they query logic
|
||||
// above only pulls in unique gateway types.. ie.. we only allow 1 credit card gateway, but many custom gateways.
|
||||
|
||||
if ($company_gateways || $company_gateways == '0') {
|
||||
@ -512,7 +510,7 @@ class Client extends BaseModel implements HasLocalePreference
|
||||
$payment_methods_intersect->push([$gateway->id => $type]);
|
||||
}
|
||||
} else {
|
||||
$payment_methods_intersect->push([$gateway->id => $type]);
|
||||
$payment_methods_intersect->push([$gateway->id => $type]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,8 @@ class Company extends BaseModel
|
||||
'invoice_task_timelog',
|
||||
'auto_start_tasks',
|
||||
'is_disabled',
|
||||
'default_task_is_date_based',
|
||||
'enable_product_discount',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\GatewayType;
|
||||
use App\PaymentDrivers\BasePaymentDriver;
|
||||
use App\Utils\Number;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
@ -236,8 +235,9 @@ class CompanyGateway extends BaseModel
|
||||
return false;
|
||||
}
|
||||
|
||||
if($gateway_type_id == GatewayType::CUSTOM)
|
||||
if ($gateway_type_id == GatewayType::CUSTOM) {
|
||||
$gateway_type_id = GatewayType::CREDIT_CARD;
|
||||
}
|
||||
|
||||
return $this->fees_and_limits->{$gateway_type_id};
|
||||
}
|
||||
@ -280,7 +280,7 @@ class CompanyGateway extends BaseModel
|
||||
|
||||
if ($fees_and_limits->fee_amount) {
|
||||
$fee += $fees_and_limits->fee_amount;
|
||||
info("fee after adding fee amount = {$fee}");
|
||||
// info("fee after adding fee amount = {$fee}");
|
||||
}
|
||||
|
||||
if ($fees_and_limits->fee_percent) {
|
||||
@ -289,7 +289,7 @@ class CompanyGateway extends BaseModel
|
||||
} else {
|
||||
$fee += round(($amount * $fees_and_limits->fee_percent / 100), 2);
|
||||
}
|
||||
info("fee after adding fee percent = {$fee}");
|
||||
// info("fee after adding fee percent = {$fee}");
|
||||
}
|
||||
|
||||
/* Cap fee if we have to here. */
|
||||
@ -303,17 +303,17 @@ class CompanyGateway extends BaseModel
|
||||
if ($include_taxes) {
|
||||
if ($fees_and_limits->fee_tax_rate1) {
|
||||
$fee += round(($pre_tax_fee * $fees_and_limits->fee_tax_rate1 / 100), 2);
|
||||
info("fee after adding fee tax 1 = {$fee}");
|
||||
// info("fee after adding fee tax 1 = {$fee}");
|
||||
}
|
||||
|
||||
if ($fees_and_limits->fee_tax_rate2) {
|
||||
$fee += round(($pre_tax_fee * $fees_and_limits->fee_tax_rate2 / 100), 2);
|
||||
info("fee after adding fee tax 2 = {$fee}");
|
||||
// info("fee after adding fee tax 2 = {$fee}");
|
||||
}
|
||||
|
||||
if ($fees_and_limits->fee_tax_rate3) {
|
||||
$fee += round(($pre_tax_fee * $fees_and_limits->fee_tax_rate3 / 100), 2);
|
||||
info("fee after adding fee tax 3 = {$fee}");
|
||||
// info("fee after adding fee tax 3 = {$fee}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,8 @@ class Country extends StaticModel
|
||||
'eea' => 'boolean',
|
||||
'swap_postal_code' => 'boolean',
|
||||
'swap_currency_symbol' => 'boolean',
|
||||
'thousand_separator' => 'string',
|
||||
'decimal_separator' => 'string',
|
||||
'updated_at' => 'timestamp',
|
||||
'created_at' => 'timestamp',
|
||||
'deleted_at' => 'timestamp',
|
||||
|
@ -17,7 +17,6 @@ use App\Events\Invoice\InvoiceWasUpdated;
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use App\Helpers\Invoice\InvoiceSumInclusive;
|
||||
use App\Jobs\Entity\CreateEntityPdf;
|
||||
use App\Models\Activity;
|
||||
use App\Models\Presenters\InvoicePresenter;
|
||||
use App\Services\Invoice\InvoiceService;
|
||||
use App\Services\Ledger\LedgerService;
|
||||
@ -438,7 +437,6 @@ class Invoice extends BaseModel
|
||||
|
||||
public function entityEmailEvent($invitation, $reminder_template)
|
||||
{
|
||||
|
||||
switch ($reminder_template) {
|
||||
case 'invoice':
|
||||
event(new InvoiceWasEmailed($invitation, $invitation->company, Ninja::eventVars()));
|
||||
|
@ -38,6 +38,7 @@ class Task extends BaseModel
|
||||
'invoice_documents',
|
||||
'rate',
|
||||
'number',
|
||||
'is_date_based',
|
||||
];
|
||||
|
||||
protected $touches = [];
|
||||
|
@ -460,4 +460,9 @@ class BaseDriver extends AbstractPaymentDriver
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCompanyGatewayId(): int
|
||||
{
|
||||
return $this->company_gateway->id;
|
||||
}
|
||||
}
|
||||
|
@ -129,8 +129,8 @@ class CreditCard
|
||||
$payment->{'3ds'} = ['enabled' => true];
|
||||
|
||||
$payment->{'success_url'} = route('payment_webhook', [
|
||||
'gateway_key' => $this->checkout->company_gateway->gateway_key,
|
||||
'company_key' => $this->checkout->client->company->company_key,
|
||||
'company_gateway_id' => $this->checkout->company_gateway->hashed_id,
|
||||
'hash' => $this->checkout->payment_hash->hash,
|
||||
]);
|
||||
}
|
||||
|
@ -28,27 +28,15 @@ class CustomPaymentDriver extends BaseDriver
|
||||
/**
|
||||
* Returns the gateway types.
|
||||
*/
|
||||
public function gatewayTypes() :array
|
||||
public function gatewayTypes(): array
|
||||
{
|
||||
$types = [
|
||||
GatewayType::CREDIT_CARD,
|
||||
GatewayType::CUSTOM,
|
||||
];
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
public function authorize($payment_method)
|
||||
{
|
||||
}
|
||||
|
||||
public function purchase($amount, $return_client_response = false)
|
||||
{
|
||||
}
|
||||
|
||||
public function refund(Payment $payment, $amount, $return_client_response = false)
|
||||
{
|
||||
}
|
||||
|
||||
public function setPaymentMethod($payment_method_id)
|
||||
{
|
||||
$this->payment_method = $payment_method_id;
|
||||
@ -56,13 +44,33 @@ class CustomPaymentDriver extends BaseDriver
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* View for displaying custom content of the driver.
|
||||
*
|
||||
* @param array $data
|
||||
* @return mixed
|
||||
*/
|
||||
public function processPaymentView($data)
|
||||
{
|
||||
return render('gateways.custom.landing_page', $data);
|
||||
$data['title'] = $this->company_gateway->getConfigField('name');
|
||||
$data['instructions'] = $this->company_gateway->getConfigField('text');
|
||||
|
||||
$this->payment_hash->data = array_merge((array) $this->payment_hash->data, $data);
|
||||
$this->payment_hash->save();
|
||||
|
||||
$data['gateway'] = $this;
|
||||
|
||||
return render('gateways.custom.payment', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processing method for payment. Should never be reached with this driver.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function processPaymentResponse($request)
|
||||
{
|
||||
return redirect()->route('client.invoices');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,13 +60,13 @@ class Alipay
|
||||
$this->stripe->payment_hash->save();
|
||||
|
||||
if ($request->redirect_status == 'succeeded') {
|
||||
return $this->processSuccesfulRedirect();
|
||||
return $this->processSuccesfulRedirect($request->source);
|
||||
}
|
||||
|
||||
return $this->processUnsuccesfulRedirect();
|
||||
}
|
||||
|
||||
public function processSuccesfulRedirect()
|
||||
public function processSuccesfulRedirect(string $source)
|
||||
{
|
||||
$this->stripe->init();
|
||||
|
||||
@ -74,7 +74,7 @@ class Alipay
|
||||
'payment_method' => $this->stripe->payment_hash->data->source,
|
||||
'payment_type' => PaymentType::ALIPAY,
|
||||
'amount' => $this->stripe->convertFromStripeAmount($this->stripe->payment_hash->data->stripe_amount, $this->stripe->client->currency()->precision),
|
||||
'transaction_reference' => ctrans('texts.n/a'),
|
||||
'transaction_reference' => $source,
|
||||
];
|
||||
|
||||
$payment = $this->stripe->createPayment($data, \App\Models\Payment::STATUS_PENDING);
|
||||
|
@ -17,8 +17,6 @@ use App\Http\Requests\Payments\PaymentWebhookRequest;
|
||||
use App\Http\Requests\Request;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentHash;
|
||||
@ -343,9 +341,9 @@ class StripePaymentDriver extends BaseDriver
|
||||
return $this->payment_method->processVerification($request, $payment_method);
|
||||
}
|
||||
|
||||
public function processWebhookRequest(PaymentWebhookRequest $request, Company $company, CompanyGateway $company_gateway, Payment $payment)
|
||||
public function processWebhookRequest(PaymentWebhookRequest $request, Payment $payment)
|
||||
{
|
||||
if ($request->type == 'source.chargable') {
|
||||
if ($request->type == 'source.chargeable') {
|
||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||
$payment->save();
|
||||
}
|
||||
|
@ -122,8 +122,8 @@ use App\Listeners\Invoice\InvoiceArchivedActivity;
|
||||
use App\Listeners\Invoice\InvoiceCancelledActivity;
|
||||
use App\Listeners\Invoice\InvoiceDeletedActivity;
|
||||
use App\Listeners\Invoice\InvoiceEmailActivity;
|
||||
use App\Listeners\Invoice\InvoiceEmailFailedActivity;
|
||||
use App\Listeners\Invoice\InvoiceEmailedNotification;
|
||||
use App\Listeners\Invoice\InvoiceEmailFailedActivity;
|
||||
use App\Listeners\Invoice\InvoicePaidActivity;
|
||||
use App\Listeners\Invoice\InvoiceReminderEmailActivity;
|
||||
use App\Listeners\Invoice\InvoiceRestoredActivity;
|
||||
@ -131,8 +131,8 @@ use App\Listeners\Invoice\InvoiceReversedActivity;
|
||||
use App\Listeners\Invoice\InvoiceViewedActivity;
|
||||
use App\Listeners\Invoice\UpdateInvoiceActivity;
|
||||
use App\Listeners\Misc\InvitationViewedListener;
|
||||
use App\Listeners\Payment\PaymentEmailFailureActivity;
|
||||
use App\Listeners\Payment\PaymentEmailedActivity;
|
||||
use App\Listeners\Payment\PaymentEmailFailureActivity;
|
||||
use App\Listeners\Payment\PaymentNotification;
|
||||
use App\Listeners\Payment\PaymentRestoredActivity;
|
||||
use App\Listeners\Quote\QuoteApprovedActivity;
|
||||
|
@ -36,14 +36,13 @@ class ApplyPayment extends AbstractService
|
||||
->ledger()
|
||||
->updatePaymentBalance($this->payment_amount * -1);
|
||||
|
||||
info("apply payment method - current client balance = {$this->payment->client->balance}");
|
||||
// info("apply payment method - current client balance = {$this->payment->client->balance}");
|
||||
|
||||
info("reducing client balance by payment amount {$this->payment_amount}");
|
||||
// info("reducing client balance by payment amount {$this->payment_amount}");
|
||||
|
||||
$this->invoice->client->service()->updateBalance($this->payment_amount * -1)->save();
|
||||
// $this->invoice->client->service()->updateBalance($this->payment_amount*-1)->updatePaidToDate($this->payment_amount)->save();
|
||||
|
||||
info("post client balance = {$this->invoice->client->balance}");
|
||||
// info("post client balance = {$this->invoice->client->balance}");
|
||||
|
||||
/* Update Pivot Record amount */
|
||||
$this->payment->invoices->each(function ($inv) {
|
||||
@ -55,8 +54,8 @@ class ApplyPayment extends AbstractService
|
||||
|
||||
$this->invoice->fresh('client');
|
||||
|
||||
info("1 end of apply payment method the client balance = {$this->invoice->client->balance}");
|
||||
|
||||
// info("1 end of apply payment method the client balance = {$this->invoice->client->balance}");
|
||||
|
||||
if ($this->invoice->hasPartial()) {
|
||||
//is partial and amount is exactly the partial amount
|
||||
if ($this->invoice->partial == $this->payment_amount) {
|
||||
@ -71,11 +70,11 @@ class ApplyPayment extends AbstractService
|
||||
} elseif ($this->payment_amount < $this->invoice->balance) { //partial invoice payment made
|
||||
$this->invoice->service()->clearPartial()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($this->payment_amount * -1);
|
||||
}
|
||||
info("2 end of apply payment method the client balnace = {$this->invoice->client->balance}");
|
||||
// info("2 end of apply payment method the client balnace = {$this->invoice->client->balance}");
|
||||
|
||||
$this->invoice->service()->applyNumber()->save();
|
||||
|
||||
info("3 end of apply payment method the client balnace = {$this->invoice->client->balance}");
|
||||
// info("3 end of apply payment method the client balnace = {$this->invoice->client->balance}");
|
||||
|
||||
return $this->invoice;
|
||||
}
|
||||
|
@ -88,6 +88,10 @@ class GenerateDeliveryNote
|
||||
|
||||
$pdf = $this->makePdf(null, null, $maker->getCompiledHTML());
|
||||
|
||||
if (config('ninja.log_pdf_html')) {
|
||||
info($maker->getCompiledHTML());
|
||||
}
|
||||
|
||||
Storage::disk($this->disk)->put($file_path, $pdf);
|
||||
|
||||
return $file_path;
|
||||
|
@ -94,6 +94,9 @@ class HandleRestore extends AbstractService
|
||||
|
||||
$new_invoice_number = substr($this->invoice->number, 0, $pos);
|
||||
|
||||
if(strlen($new_invoice_number) == 0)
|
||||
$new_invoice_number = null;
|
||||
|
||||
try {
|
||||
$this->invoice->number = $new_invoice_number;
|
||||
$this->invoice->save();
|
||||
|
@ -134,7 +134,7 @@ class Design extends BaseDesign
|
||||
$elements = [];
|
||||
|
||||
foreach ($variables as $variable) {
|
||||
$elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false];
|
||||
$elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_details-' . substr($variable, 1)]];
|
||||
}
|
||||
|
||||
return $elements;
|
||||
@ -147,7 +147,7 @@ class Design extends BaseDesign
|
||||
$elements = [];
|
||||
|
||||
foreach ($variables as $variable) {
|
||||
$elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false];
|
||||
$elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_address-' . substr($variable, 1)]];
|
||||
}
|
||||
|
||||
return $elements;
|
||||
@ -159,15 +159,19 @@ class Design extends BaseDesign
|
||||
|
||||
if ($this->type == 'delivery_note') {
|
||||
$elements = [
|
||||
['element' => 'p', 'content' => $this->entity->client->name, 'show_empty' => false],
|
||||
['element' => 'p', 'content' => $this->entity->client->shipping_address1, 'show_empty' => false],
|
||||
['element' => 'p', 'content' => $this->entity->client->shipping_address2, 'show_empty' => false],
|
||||
['element' => 'p', 'content' => "{$this->entity->client->shipping_city} {$this->entity->client->shipping_state} {$this->entity->client->shipping_postal_code}", 'show_empty' => false],
|
||||
['element' => 'p', 'content' => $this->entity->client->name, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.name']],
|
||||
['element' => 'p', 'content' => $this->entity->client->shipping_address1, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address1']],
|
||||
['element' => 'p', 'content' => $this->entity->client->shipping_address2, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address2']],
|
||||
['element' => 'p', 'show_empty' => false, 'elements' => [
|
||||
['element' => 'span', 'content' => "{$this->entity->client->shipping_city} ", 'properties' => ['ref' => 'delivery_note-client.shipping_city']],
|
||||
['element' => 'span', 'content' => "{$this->entity->client->shipping_state} ", 'properties' => ['ref' => 'delivery_note-client.shipping_state']],
|
||||
['element' => 'span', 'content' => "{$this->entity->client->shipping_postal_code} ", 'properties' => ['ref' => 'delivery_note-client.shipping_postal_code']],
|
||||
]],
|
||||
['element' => 'p', 'content' => optional($this->entity->client->shipping_country)->name, 'show_empty' => false],
|
||||
];
|
||||
|
||||
if (!is_null($this->context['contact'])) {
|
||||
$elements[] = ['element' => 'p', 'content' => $this->context['contact']->email, 'show_empty' => false];
|
||||
$elements[] = ['element' => 'p', 'content' => $this->context['contact']->email, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-contact.email']];
|
||||
}
|
||||
|
||||
return $elements;
|
||||
@ -176,7 +180,7 @@ class Design extends BaseDesign
|
||||
$variables = $this->context['pdf_variables']['client_details'];
|
||||
|
||||
foreach ($variables as $variable) {
|
||||
$elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false];
|
||||
$elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'client_details-' . substr($variable, 1)]];
|
||||
}
|
||||
|
||||
return $elements;
|
||||
@ -205,13 +209,13 @@ class Design extends BaseDesign
|
||||
|
||||
if (in_array($_variable, $_customs)) {
|
||||
$elements[] = ['element' => 'tr', 'elements' => [
|
||||
['element' => 'th', 'content' => $variable . '_label'],
|
||||
['element' => 'th', 'content' => $variable],
|
||||
['element' => 'th', 'content' => $variable . '_label', 'properties' => ['data-ref' => 'entity_details-' . substr($variable, 1) . '_label']],
|
||||
['element' => 'th', 'content' => $variable, 'properties' => ['data-ref' => 'entity_details-' . substr($variable, 1)]],
|
||||
]];
|
||||
} else {
|
||||
$elements[] = ['element' => 'tr', 'properties' => ['hidden' => $this->entityVariableCheck($variable)], 'elements' => [
|
||||
['element' => 'th', 'content' => $variable . '_label'],
|
||||
['element' => 'th', 'content' => $variable],
|
||||
['element' => 'th', 'content' => $variable . '_label', 'properties' => ['data-ref' => 'entity_details-' . substr($variable, 1) . '_label']],
|
||||
['element' => 'th', 'content' => $variable, 'properties' => ['data-ref' => 'entity_details-' . substr($variable, 1)]],
|
||||
]];
|
||||
}
|
||||
}
|
||||
@ -225,16 +229,14 @@ class Design extends BaseDesign
|
||||
return [];
|
||||
}
|
||||
|
||||
$elements = [
|
||||
return [
|
||||
['element' => 'thead', 'elements' => [
|
||||
['element' => 'th', 'content' => '$item_label'],
|
||||
['element' => 'th', 'content' => '$description_label'],
|
||||
['element' => 'th', 'content' => '$product.quantity_label'],
|
||||
['element' => 'th', 'content' => '$item_label', 'properties' => ['data-ref' => 'delivery_note-item_label']],
|
||||
['element' => 'th', 'content' => '$description_label', 'properties' => ['data-ref' => 'delivery_note-description_label']],
|
||||
['element' => 'th', 'content' => '$product.quantity_label', 'properties' => ['data-ref' => 'delivery_note-product.quantity_label']],
|
||||
]],
|
||||
['element' => 'tbody', 'elements' => $this->buildTableBody('delivery_note')],
|
||||
];
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -256,7 +258,7 @@ class Design extends BaseDesign
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
return [
|
||||
['element' => 'thead', 'elements' => $this->buildTableHeader('product')],
|
||||
['element' => 'tbody', 'elements' => $this->buildTableBody('$product')],
|
||||
];
|
||||
@ -306,7 +308,7 @@ class Design extends BaseDesign
|
||||
if (array_key_exists($column, $aliases)) {
|
||||
$elements[] = ['element' => 'th', 'content' => $aliases[$column] . '_label'];
|
||||
} else {
|
||||
$elements[] = ['element' => 'th', 'content' => $column . '_label'];
|
||||
$elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th']];
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,9 +335,9 @@ class Design extends BaseDesign
|
||||
foreach ($items as $row) {
|
||||
$element = ['element' => 'tr', 'elements' => []];
|
||||
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.product_key']];
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.notes']];
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.quantity']];
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.product_key'], 'properties' => ['data-ref' => 'delivery_note_table.product_key-td']];
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.notes'], 'properties' => ['data-ref' => 'delivery_note_table.notes-td']];
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.quantity'], 'properties' => ['data-ref' => 'delivery_note_table.quantity-td']];
|
||||
|
||||
$elements[] = $element;
|
||||
}
|
||||
@ -378,21 +380,21 @@ class Design extends BaseDesign
|
||||
// $task.quantity => $task.hours
|
||||
|
||||
if ($cell == '$task.rate') {
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row['$task.cost']];
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row['$task.cost'], 'properties' => ['data-ref' => 'task_table-task.cost-td']];
|
||||
} elseif ($cell == '$task.hours') {
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row['$task.quantity']];
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row['$task.quantity'], 'properties' => ['data-ref' => 'task_table-task.hours-td']];
|
||||
} elseif ($cell == '$task.description') {
|
||||
$_element = ['element' => 'td', 'content' => '', 'elements' => [
|
||||
['element' => 'span', 'content' => $row[$cell]],
|
||||
['element' => 'span', 'content' => $row[$cell], 'properties' => ['data-ref' => 'task_table-task.description-td']],
|
||||
]];
|
||||
|
||||
foreach ($this->getTaskTimeLogs($row) as $log) {
|
||||
$_element['elements'][] = ['element' => 'span', 'content' => $log, 'properties' => ['class' => 'task-duration']];
|
||||
$_element['elements'][] = ['element' => 'span', 'content' => $log, 'properties' => ['class' => 'task-duration', 'data-ref' => 'task_table-task.duration']];
|
||||
}
|
||||
|
||||
$element['elements'][] = $_element;
|
||||
} else {
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell]];
|
||||
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => "{$_type}_table-" . substr($cell, 1) . '-td']];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -413,7 +415,7 @@ class Design extends BaseDesign
|
||||
|
||||
$elements = [
|
||||
['element' => 'div', 'elements' => [
|
||||
['element' => 'span', 'content' => '$entity.public_notes', 'properties' => ['data-element' => 'total-table-public-notes-label']],
|
||||
['element' => 'span', 'content' => '$entity.public_notes', 'properties' => ['data-ref' => 'total_table-public_notes-label', 'style' => 'text-align: left;']],
|
||||
]],
|
||||
];
|
||||
|
||||
@ -465,7 +467,7 @@ class Design extends BaseDesign
|
||||
} else {
|
||||
$elements[] = ['element' => 'div', 'elements' => [
|
||||
['element' => 'span', 'content' => 'This is placeholder for the 3rd fraction of element.', 'properties' => ['style' => 'opacity: 0%']], // Placeholder for fraction of element (3fr)
|
||||
['element' => 'span', 'content' => $variable . '_label'],
|
||||
['element' => 'span', 'content' => $variable . '_label', 'properties' => ['data-ref' => 'totals_table-' . substr($variable, 1)]],
|
||||
['element' => 'span', 'content' => $variable],
|
||||
]];
|
||||
}
|
||||
|
@ -146,6 +146,8 @@ class CompanyTransformer extends EntityTransformer
|
||||
'invoice_task_documents' => (bool) $company->invoice_task_documents,
|
||||
'show_tasks_table' => (bool) $company->show_tasks_table,
|
||||
'use_credits_payment' => 'always', //todo remove
|
||||
'default_task_is_date_based' => (bool)$company->default_task_is_date_based,
|
||||
'enable_product_discount' => (bool)$company->enable_product_discount,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ class ExpenseTransformer extends EntityTransformer
|
||||
'bank_id' => (string) $expense->bank_id ?: '',
|
||||
'invoice_currency_id' => (string) $expense->invoice_currency_id ?: '',
|
||||
'expense_currency_id' => '', //todo remove redundant in 5.0.25
|
||||
'currency_id' => (string) $expense->expense_currency_id ?: '',
|
||||
'currency_id' => (string) $expense->currency_id ?: '',
|
||||
'category_id' => $this->encodePrimaryKey($expense->category_id),
|
||||
'payment_type_id' => (string) $expense->payment_type_id ?: '',
|
||||
'recurring_expense_id' => (string) $expense->recurring_expense_id ?: '',
|
||||
|
@ -48,7 +48,7 @@ class ProjectTransformer extends EntityTransformer
|
||||
'assigned_user_id' => (string) $this->encodePrimaryKey($project->assigned_user_id),
|
||||
'client_id' => (string) $this->encodePrimaryKey($project->client_id),
|
||||
'name' => $project->name ?: '',
|
||||
'number' => $project->number,
|
||||
'number' => $project->number ?: '',
|
||||
'created_at' => (int) $project->created_at,
|
||||
'updated_at' => (int) $project->updated_at,
|
||||
'archived_at' => (int) $project->deleted_at,
|
||||
|
@ -66,6 +66,7 @@ class TaskTransformer extends EntityTransformer
|
||||
'custom_value4' => $task->custom_value4 ?: '',
|
||||
'status_id' => $this->encodePrimaryKey($task->status_id) ?: '',
|
||||
'status_sort_order' => (int) $task->status_sort_order,
|
||||
'is_date_based' => (bool) $task->is_date_based,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -92,8 +92,7 @@ class HtmlEngine
|
||||
}
|
||||
|
||||
$data = [];
|
||||
$data['$global-margin'] = ['value' => 'm-8', 'label' => ''];
|
||||
$data['$global-padding'] = ['value' => 'p-8', 'label' => ''];
|
||||
$data['$global_margin'] = ['value' => config('ninja.experimental_pdf_engine') ? '0cm' : '1cm', 'label' => ''];
|
||||
$data['$tax'] = ['value' => '', 'label' => ctrans('texts.tax')];
|
||||
$data['$app_url'] = ['value' => $this->generateAppUrl(), 'label' => ''];
|
||||
$data['$from'] = ['value' => '', 'label' => ctrans('texts.from')];
|
||||
@ -155,7 +154,7 @@ class HtmlEngine
|
||||
} else {
|
||||
$data['$balance_due'] = ['value' => Number::formatMoney($this->entity->balance, $this->client) ?: ' ', 'label' => ctrans('texts.balance_due')];
|
||||
}
|
||||
|
||||
|
||||
$data['$quote.balance_due'] = $data['$balance_due'];
|
||||
$data['$invoice.balance_due'] = $data['$balance_due'];
|
||||
$data['$balance_due'] = $data['$balance_due'];
|
||||
@ -184,7 +183,7 @@ class HtmlEngine
|
||||
$data['$invoice.custom2'] = ['value' => $this->formatCustomFieldValue('invoice2', $this->entity->custom_value2) ?: ' ', 'label' => $this->makeCustomField('invoice2')];
|
||||
$data['$invoice.custom3'] = ['value' => $this->formatCustomFieldValue('invoice3', $this->entity->custom_value3) ?: ' ', 'label' => $this->makeCustomField('invoice3')];
|
||||
$data['$invoice.custom4'] = ['value' => $this->formatCustomFieldValue('invoice4', $this->entity->custom_value4) ?: ' ', 'label' => $this->makeCustomField('invoice4')];
|
||||
$data['$invoice.public_notes'] = ['value' => $this->entity->public_notes ?: ' ', 'label' => ctrans('texts.public_notes')];
|
||||
$data['$invoice.public_notes'] = ['value' => nl2br($this->entity->public_notes) ?: ' ', 'label' => ctrans('texts.public_notes')];
|
||||
$data['$entity.public_notes'] = &$data['$invoice.public_notes'];
|
||||
|
||||
$data['$entity_issued_to'] = ['value' => '', 'label' => ctrans("texts.{$this->entity_string}_issued_to")];
|
||||
@ -243,7 +242,7 @@ class HtmlEngine
|
||||
$data['$client.postal_city_state'] = &$data['$postal_city_state'];
|
||||
$data['$client.country'] = &$data['$country'];
|
||||
$data['$client.email'] = &$data['$email'];
|
||||
|
||||
|
||||
$data['$client.currency'] = ['value' => $this->client->currency()->code, 'label' => ''];
|
||||
|
||||
$data['$client.balance'] = ['value' => Number::formatMoney($this->client->balance, $this->client), 'label' => ctrans('texts.account_balance')];
|
||||
|
@ -28,7 +28,7 @@ class SystemHealth
|
||||
'gd',
|
||||
'curl',
|
||||
'zip',
|
||||
'gmp',
|
||||
// 'gmp',
|
||||
'openssl',
|
||||
'mbstring',
|
||||
'xml',
|
||||
@ -78,9 +78,29 @@ class SystemHealth
|
||||
'node_status' => self::checkNode(),
|
||||
'cache_enabled' => self::checkConfigCache(),
|
||||
'phantom_enabled' => (bool) config('ninja.phantomjs_pdf_generation'),
|
||||
'exec' => (bool) self::checkExecWorks(),
|
||||
'open_basedir' => (bool) self::checkOpenBaseDir(),
|
||||
];
|
||||
}
|
||||
|
||||
public static function checkOpenBaseDir()
|
||||
{
|
||||
if (strlen(ini_get('open_basedir') == 0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function checkExecWorks()
|
||||
{
|
||||
if (function_exists('exec')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function checkConfigCache()
|
||||
{
|
||||
if (env('APP_URL')) {
|
||||
@ -100,7 +120,6 @@ class SystemHealth
|
||||
}
|
||||
|
||||
return 'Node not found.';
|
||||
|
||||
} catch (Exception $e) {
|
||||
return 'Node not found.';
|
||||
}
|
||||
@ -116,7 +135,6 @@ class SystemHealth
|
||||
}
|
||||
|
||||
return 'NPM not found';
|
||||
|
||||
} catch (Exception $e) {
|
||||
return 'NPM not found';
|
||||
}
|
||||
|
@ -63,6 +63,15 @@ trait ClientGroupSettingsSaver
|
||||
$entity_settings->{$key} = $value;
|
||||
}
|
||||
|
||||
//this pass will handle any null values that are in the translations
|
||||
foreach ($settings->translations as $key => $value) {
|
||||
if (is_null($settings->translations[$key])) {
|
||||
$settings->translations[$key] = '';
|
||||
}
|
||||
}
|
||||
|
||||
$entity_settings->translations = $settings->translations;
|
||||
|
||||
$entity->settings = $entity_settings;
|
||||
$entity->save();
|
||||
|
||||
|
@ -58,6 +58,15 @@ trait CompanySettingsSaver
|
||||
}
|
||||
}
|
||||
|
||||
//this pass will handle any null values that are in the translations
|
||||
foreach ($settings->translations as $key => $value) {
|
||||
if (is_null($settings->translations[$key])) {
|
||||
$settings->translations[$key] = '';
|
||||
}
|
||||
}
|
||||
|
||||
$company_settings->translations = $settings->translations;
|
||||
|
||||
$entity->settings = $company_settings;
|
||||
|
||||
$entity->save();
|
||||
|
@ -209,7 +209,4 @@ trait MakesReminders
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Utils\Traits\Pdf;
|
||||
|
||||
//use Beganovich\ChromiumPdf\ChromiumPdf;
|
||||
use Spatie\Browsershot\Browsershot;
|
||||
|
||||
trait PdfMaker
|
||||
@ -26,6 +27,15 @@ trait PdfMaker
|
||||
*/
|
||||
public function makePdf($header, $footer, $html)
|
||||
{
|
||||
if (config('ninja.experimental_pdf_engine')) {
|
||||
$pdf = new ChromiumPdf();
|
||||
|
||||
return $pdf
|
||||
->setChromiumPath(config('ninja.experimental_pdf_engine_chromium_path'))
|
||||
->setHtml($html)
|
||||
->generate();
|
||||
}
|
||||
|
||||
$browser = Browsershot::html($html);
|
||||
|
||||
if (config('ninja.system.node_path')) {
|
||||
@ -44,34 +54,3 @@ trait PdfMaker
|
||||
->pdf();
|
||||
}
|
||||
}
|
||||
|
||||
// if($header && $footer){
|
||||
// $browser = Browsershot::html($html)
|
||||
// ->headerHtml($header)
|
||||
// ->footerHtml($footer);
|
||||
// }
|
||||
// elseif($header){
|
||||
// $browser = Browsershot::html($html)
|
||||
// ->headerHtml($header);
|
||||
// }
|
||||
// else if($footer){
|
||||
// $browser = Browsershot::html($html)
|
||||
// ->footerHtml($footer);
|
||||
// }
|
||||
// else {
|
||||
// $browser = Browsershot::html($html);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // return Browsershot::html($html)
|
||||
// //->showBrowserHeaderAndFooter()
|
||||
// //->headerHtml($header)
|
||||
// //->footerHtml($footer)
|
||||
// ->deviceScaleFactor(1)
|
||||
// ->showBackground()
|
||||
// ->waitUntilNetworkIdle(true) ->pdf();
|
||||
// //->margins(10,10,10,10)
|
||||
// //->savePdf('test.pdf');
|
||||
//
|
||||
// $browser->format('A4');
|
||||
// $browser->landscape();
|
||||
|
@ -10,7 +10,8 @@
|
||||
"CRM",
|
||||
"Credit card billing",
|
||||
"projects",
|
||||
"tasks"
|
||||
"tasks",
|
||||
"freelancer"
|
||||
],
|
||||
"license": "Attribution Assurance License",
|
||||
"authors": [
|
||||
@ -27,8 +28,10 @@
|
||||
"require": {
|
||||
"php": "^7.3|^7.4",
|
||||
"ext-json": "*",
|
||||
"ext-libxml": "*",
|
||||
"asgrim/ofxparser": "^1.2",
|
||||
"authorizenet/authorizenet": "^2.0",
|
||||
"beganovich/chromium-pdf": "dev-master",
|
||||
"checkout/checkout-sdk-php": "^1.0",
|
||||
"cleverit/ubl_invoice": "^1.3",
|
||||
"composer/composer": "^2",
|
||||
@ -47,10 +50,11 @@
|
||||
"laravel/socialite": "^5",
|
||||
"laravel/tinker": "^2.0",
|
||||
"laravel/ui": "^3.0",
|
||||
"league/csv": "^9.6",
|
||||
"league/flysystem-aws-s3-v3": "~1.0",
|
||||
"league/flysystem-cached-adapter": "^1.1",
|
||||
"league/fractal": "^0.17.0",
|
||||
"league/omnipay": "^3.0",
|
||||
"league/omnipay": "^3",
|
||||
"livewire/livewire": "^2.0",
|
||||
"maennchen/zipstream-php": "^1.2",
|
||||
"nwidart/laravel-modules": "^8.0",
|
||||
@ -61,7 +65,8 @@
|
||||
"stripe/stripe-php": "^7.50",
|
||||
"turbo124/beacon": "^1",
|
||||
"turbo124/laravel-gmail": "^5.0",
|
||||
"webpatser/laravel-countries": "dev-master#75992ad"
|
||||
"webpatser/laravel-countries": "dev-master#75992ad",
|
||||
"ext-dom": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"anahkiasen/former": "^4.2",
|
||||
|
450
composer.lock
generated
@ -7,12 +7,12 @@ return [
|
||||
'production' => env('NINJA_PROD', false),
|
||||
'license' => env('NINJA_LICENSE', ''),
|
||||
'version_url' => 'https://raw.githubusercontent.com/invoiceninja/invoiceninja/v5-stable/VERSION.txt',
|
||||
'app_name' => env('APP_NAME'),
|
||||
'app_name' => env('APP_NAME','Invoice Ninja'),
|
||||
'app_env' => env('APP_ENV', 'selfhosted'),
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', ''),
|
||||
'app_version' => '5.0.35',
|
||||
'app_version' => '5.0.36',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', false),
|
||||
@ -32,7 +32,7 @@ return [
|
||||
'phantomjs_secret' => env('PHANTOMJS_SECRET', false),
|
||||
'phantomjs_pdf_generation' => env('PHANTOMJS_PDF_GENERATION', true),
|
||||
'trusted_proxies' => env('TRUSTED_PROXIES', false),
|
||||
|
||||
|
||||
'sentry_dsn' => env('SENTRY_LARAVEL_DSN', 'https://9b4e15e575214354a7d666489783904a@sentry.invoicing.co/6'),
|
||||
'environment' => env('NINJA_ENVIRONMENT', 'selfhost'), // 'hosted', 'development', 'selfhost', 'reseller'
|
||||
|
||||
@ -135,4 +135,7 @@ return [
|
||||
'designs' => [
|
||||
'base_path' => resource_path('views/pdf-designs/'),
|
||||
],
|
||||
'experimental_pdf_engine' => env('EXPERIMENTAL_PDF_ENGINE', false),
|
||||
'experimental_pdf_engine_chromium_path' => env('EXPERIMENTAL_PDF_ENGINE_CHROMIUM_PATH', null),
|
||||
'log_pdf_html' => env('LOG_PDF_HTML', false),
|
||||
];
|
||||
|
34
database/migrations/2020_12_14_114722_task_fields.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class TaskFields extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('tasks', function (Blueprint $table) {
|
||||
$table->boolean('is_date_based')->default(false);
|
||||
});
|
||||
|
||||
Schema::table('companies', function (Blueprint $table) {
|
||||
$table->boolean('default_task_is_date_based')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddEnableProductDiscountFieldToCompaniesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('companies', function (Blueprint $table) {
|
||||
$table->boolean('enable_product_discount')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('companies', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
}
|
0
public/.htaccess
Normal file → Executable file
0
public/assets/AssetManifest.json
Normal file → Executable file
0
public/assets/FontManifest.json
Normal file → Executable file
0
public/assets/LICENSE
Normal file → Executable file
49
public/assets/NOTICES
Normal file → Executable file
@ -6032,6 +6032,31 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
--------------------------------------------------------------------------------
|
||||
draggable_scrollbar
|
||||
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2018 Draggable Scrollbar Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
engine
|
||||
gpu
|
||||
@ -6226,6 +6251,30 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
--------------------------------------------------------------------------------
|
||||
file_picker
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Miguel Ruivo
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
--------------------------------------------------------------------------------
|
||||
files
|
||||
|
||||
Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
|
||||
|
0
public/assets/assets/images/google-icon.png
Normal file → Executable file
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
0
public/assets/assets/images/logo.png
Normal file → Executable file
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
0
public/assets/assets/images/payment_types/ach.png
Normal file → Executable file
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
0
public/assets/assets/images/payment_types/amex.png
Normal file → Executable file
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
0
public/assets/assets/images/payment_types/carteblanche.png
Normal file → Executable file
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
0
public/assets/assets/images/payment_types/dinerscard.png
Normal file → Executable file
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
0
public/assets/assets/images/payment_types/discover.png
Normal file → Executable file
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
0
public/assets/assets/images/payment_types/jcb.png
Normal file → Executable file
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
0
public/assets/assets/images/payment_types/laser.png
Normal file → Executable file
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
0
public/assets/assets/images/payment_types/maestro.png
Normal file → Executable file
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
0
public/assets/assets/images/payment_types/mastercard.png
Normal file → Executable file
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
0
public/assets/assets/images/payment_types/other.png
Normal file → Executable file
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
0
public/assets/assets/images/payment_types/paypal.png
Normal file → Executable file
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
0
public/assets/assets/images/payment_types/solo.png
Normal file → Executable file
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
0
public/assets/assets/images/payment_types/switch.png
Normal file → Executable file
Before Width: | Height: | Size: 938 B After Width: | Height: | Size: 938 B |
0
public/assets/assets/images/payment_types/unionpay.png
Normal file → Executable file
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
0
public/assets/assets/images/payment_types/visa.png
Normal file → Executable file
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
0
public/assets/fonts/MaterialIcons-Regular.otf
Normal file → Executable file
0
public/assets/fonts/MaterialIcons-Regular.ttf
Normal file → Executable file
0
public/assets/fonts/Roboto-Regular.ttf
Normal file → Executable file
0
public/assets/packages/flutter_auth_buttons/fonts/SF-Pro-Medium.ttf
Normal file → Executable file
0
public/assets/packages/flutter_auth_buttons/graphics/apple_logo_black.png
Normal file → Executable file
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |