Merge pull request #7186 from turbo124/v5-stable

v5.3.56
This commit is contained in:
David Bomba 2022-02-07 19:16:38 +11:00 committed by GitHub
commit 3ad68e264d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 365202 additions and 362995 deletions

View File

@ -1 +1 @@
5.3.55
5.3.56

View File

@ -13,6 +13,10 @@ namespace App\Factory;
use App\Models\Expense;
use App\Models\RecurringExpense;
use App\Utils\Helpers;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
class RecurringExpenseToExpenseFactory
{
@ -21,6 +25,7 @@ class RecurringExpenseToExpenseFactory
$expense = new Expense();
$expense->user_id = $recurring_expense->user_id;
$expense->assigned_user_id = $recurring_expense->assigned_user_id;
$expense->client_id = $recurring_expense->client_id;
$expense->vendor_id = $recurring_expense->vendor_id;
$expense->invoice_id = $recurring_expense->invoice_id;
$expense->currency_id = $recurring_expense->currency_id;
@ -39,8 +44,12 @@ class RecurringExpenseToExpenseFactory
$expense->payment_date = $recurring_expense->payment_date;
$expense->amount = $recurring_expense->amount;
$expense->foreign_amount = $recurring_expense->foreign_amount ?: 0;
$expense->private_notes = $recurring_expense->private_notes;
$expense->public_notes = $recurring_expense->public_notes;
// $expense->private_notes = $recurring_expense->private_notes;
// $expense->public_notes = $recurring_expense->public_notes;
$expense->public_notes = self::transformObject($recurring_expense->public_notes, $recurring_expense);
$expense->private_notes = self::transformObject($recurring_expense->private_notes, $recurring_expense);
$expense->transaction_reference = $recurring_expense->transaction_reference;
$expense->custom_value1 = $recurring_expense->custom_value1;
$expense->custom_value2 = $recurring_expense->custom_value2;
@ -59,4 +68,179 @@ class RecurringExpenseToExpenseFactory
return $expense;
}
public static function transformObject(?string $value, $recurring_expense): ?string
{
if(!$value)
return '';
if($recurring_expense->client){
$locale = $recurring_expense->client->locale();
$date_format = $recurring_expense->client->date_format();
}
else {
$locale = $recurring_expense->company->locale();
$date_formats = Cache::get('date_formats');
$date_format = $date_formats->filter(function ($item) use($recurring_expense){
return $item->id == $recurring_expense->company->settings->date_format_id;
})->first()->format;
}
Carbon::setLocale($locale);
$replacements = [
'literal' => [
':MONTH' => Carbon::createFromDate(now()->year, now()->month)->translatedFormat('F'),
':YEAR' => now()->year,
':QUARTER' => 'Q' . now()->quarter,
':WEEK_BEFORE' => \sprintf(
'%s %s %s',
Carbon::now()->subDays(7)->translatedFormat($date_format),
ctrans('texts.to'),
Carbon::now()->translatedFormat($date_format)
),
':WEEK_AHEAD' => \sprintf(
'%s %s %s',
Carbon::now()->addDays(7)->translatedFormat($date_format),
ctrans('texts.to'),
Carbon::now()->addDays(14)->translatedFormat($date_format)
),
':WEEK' => \sprintf(
'%s %s %s',
Carbon::now()->translatedFormat($date_format),
ctrans('texts.to'),
Carbon::now()->addDays(7)->translatedFormat($date_format)
),
],
'raw' => [
':MONTH' => now()->month,
':YEAR' => now()->year,
':QUARTER' => now()->quarter,
],
'ranges' => [
'MONTHYEAR' => Carbon::createFromDate(now()->year, now()->month),
],
'ranges_raw' => [
'MONTH' => now()->month,
'YEAR' => now()->year,
],
];
// First case, with ranges.
preg_match_all('/\[(.*?)]/', $value, $ranges);
$matches = array_shift($ranges);
foreach ($matches as $match) {
if (!Str::contains($match, '|')) {
continue;
}
if (Str::contains($match, '|')) {
$parts = explode('|', $match); // [ '[MONTH', 'MONTH+2]' ]
$left = substr($parts[0], 1); // 'MONTH'
$right = substr($parts[1], 0, -1); // MONTH+2
// If left side is not part of replacements, skip.
if (!array_key_exists($left, $replacements['ranges'])) {
continue;
}
$_left = Carbon::createFromDate(now()->year, now()->month)->translatedFormat('F Y');
$_right = '';
// If right side doesn't have any calculations, replace with raw ranges keyword.
if (!Str::contains($right, ['-', '+', '/', '*'])) {
$_right = Carbon::createFromDate(now()->year, now()->month)->translatedFormat('F Y');
}
// If right side contains one of math operations, calculate.
if (Str::contains($right, ['+'])) {
$operation = preg_match_all('/(?!^-)[+*\/-](\s?-)?/', $right, $_matches);
$_operation = array_shift($_matches)[0]; // + -
$_value = explode($_operation, $right); // [MONTHYEAR, 4]
$_right = Carbon::createFromDate(now()->year, now()->month)->addMonths($_value[1])->translatedFormat('F Y');
}
$replacement = sprintf('%s to %s', $_left, $_right);
$value = preg_replace(
sprintf('/%s/', preg_quote($match)), $replacement, $value, 1
);
}
}
// Second case with more common calculations.
preg_match_all('/:([^:\s]+)/', $value, $common);
$matches = array_shift($common);
foreach ($matches as $match) {
$matches = collect($replacements['literal'])->filter(function ($value, $key) use ($match) {
return Str::startsWith($match, $key);
});
if ($matches->count() === 0) {
continue;
}
if (!Str::contains($match, ['-', '+', '/', '*'])) {
$value = preg_replace(
sprintf('/%s/', $matches->keys()->first()), $replacements['literal'][$matches->keys()->first()], $value, 1
);
}
if (Str::contains($match, ['-', '+', '/', '*'])) {
$operation = preg_match_all('/(?!^-)[+*\/-](\s?-)?/', $match, $_matches);
$_operation = array_shift($_matches)[0];
$_value = explode($_operation, $match); // [:MONTH, 4]
$raw = strtr($matches->keys()->first(), $replacements['raw']); // :MONTH => 1
$number = $res = preg_replace("/[^0-9]/", '', $_value[1]); // :MONTH+1. || :MONTH+2! => 1 || 2
$target = "/{$matches->keys()->first()}\\{$_operation}{$number}/"; // /:$KEYWORD\\$OPERATION$VALUE => /:MONTH\\+1
$output = (int) $raw + (int)$_value[1];
if ($operation == '+') {
$output = (int) $raw + (int)$_value[1]; // 1 (:MONTH) + 4
}
if ($_operation == '-') {
$output = (int)$raw - (int)$_value[1]; // 1 (:MONTH) - 4
}
if ($_operation == '/' && (int)$_value[1] != 0) {
$output = (int)$raw / (int)$_value[1]; // 1 (:MONTH) / 4
}
if ($_operation == '*') {
$output = (int)$raw * (int)$_value[1]; // 1 (:MONTH) * 4
}
if ($matches->keys()->first() == ':MONTH') {
$output = \Carbon\Carbon::create()->month($output)->translatedFormat('F');
}
$value = preg_replace(
$target, $output, $value, 1
);
}
}
return $value;
}
}

View File

@ -134,21 +134,22 @@ class InvoiceItemSum
$item_tax += $item_tax_rate1_total;
if($item_tax_rate1_total != 0)
$this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total);
// if($item_tax_rate1_total != 0)
if (strlen($this->item->tax_name1) > 1)
$this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total);
$item_tax_rate2_total = $this->calcAmountLineTax($this->item->tax_rate2, $amount);
$item_tax += $item_tax_rate2_total;
if($item_tax_rate2_total != 0)
if (strlen($this->item->tax_name2) > 1)
$this->groupTax($this->item->tax_name2, $this->item->tax_rate2, $item_tax_rate2_total);
$item_tax_rate3_total = $this->calcAmountLineTax($this->item->tax_rate3, $amount);
$item_tax += $item_tax_rate3_total;
if($item_tax_rate3_total != 0)
if (strlen($this->item->tax_name3) > 1)
$this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total);
$this->setTotalTaxes($this->formatValue($item_tax, $this->currency->precision));

View File

@ -119,21 +119,22 @@ class InvoiceItemSumInclusive
$item_tax += $this->formatValue($item_tax_rate1_total, $this->currency->precision);
if($item_tax_rate1_total != 0)
// if($item_tax_rate1_total != 0)
if (strlen($this->item->tax_name1) > 1)
$this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total);
$item_tax_rate2_total = $this->calcInclusiveLineTax($this->item->tax_rate2, $amount);
$item_tax += $this->formatValue($item_tax_rate2_total, $this->currency->precision);
if($item_tax_rate2_total != 0)
if (strlen($this->item->tax_name2) > 1)
$this->groupTax($this->item->tax_name2, $this->item->tax_rate2, $item_tax_rate2_total);
$item_tax_rate3_total = $this->calcInclusiveLineTax($this->item->tax_rate3, $amount);
$item_tax += $this->formatValue($item_tax_rate3_total, $this->currency->precision);
if($item_tax_rate3_total != 0)
if (strlen($this->item->tax_name3) > 1)
$this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total);

View File

@ -35,17 +35,17 @@ class ExpenseMap
{
return [
0 => 'texts.vendor',
1 => 'texts.client',
2 => 'texts.project',
3 => 'texts.category',
4 => 'texts.amount',
5 => 'texts.currency',
6 => 'texts.date',
7 => 'texts.payment_type',
8 => 'texts.payment_date',
9 => 'texts.transaction_reference',
10 => 'texts.public_notes',
11 => 'texts.private_notes',
1 => 'texts.client',
2 => 'texts.project',
3 => 'texts.category',
4 => 'texts.amount',
5 => 'texts.currency',
6 => 'texts.date',
7 => 'texts.payment_type',
8 => 'texts.payment_date',
9 => 'texts.transaction_reference',
10 => 'texts.public_notes',
11 => 'texts.private_notes',
];
}
}

View File

@ -10,10 +10,17 @@
*/
namespace App\Import\Providers;
use App\Factory\ClientFactory;
use App\Factory\InvoiceFactory;
use App\Factory\PaymentFactory;
use App\Http\Requests\Invoice\StoreInvoiceRequest;
use App\Import\ImportException;
use App\Models\Company;
use App\Models\Invoice;
use App\Models\User;
use App\Repositories\ClientRepository;
use App\Repositories\InvoiceRepository;
use App\Repositories\PaymentRepository;
use App\Utils\Traits\CleanLineItems;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
@ -23,10 +30,10 @@ use League\Csv\Reader;
use League\Csv\Statement;
use Symfony\Component\HttpFoundation\ParameterBag;
class BaseImport {
class BaseImport
{
use CleanLineItems;
public Company $company;
public array $request;
@ -35,54 +42,62 @@ class BaseImport {
public $request_name;
public $repository_name;
public $repository_name;
public $factory_name;
public $factory_name;
public $repository;
public $repository;
public $transformer;
public $transformer;
public function __construct( array $request, Company $company ) {
$this->company = $company;
$this->request = $request;
$this->hash = $request['hash'];
$this->import_type = $request['import_type'];
$this->skip_header = $request['skip_header'] ?? null;
$this->column_map =
! empty( $request['column_map'] ) ?
array_combine( array_keys( $request['column_map'] ), array_column( $request['column_map'], 'mapping' ) ) : null;
public function __construct(array $request, Company $company)
{
$this->company = $company;
$this->request = $request;
$this->hash = $request['hash'];
$this->import_type = $request['import_type'];
$this->skip_header = $request['skip_header'] ?? null;
$this->column_map = !empty($request['column_map'])
? array_combine(
array_keys($request['column_map']),
array_column($request['column_map'], 'mapping')
)
: null;
auth()->login( $this->company->owner(), true );
auth()->user()->setCompany($this->company);
}
auth()->login($this->company->owner(), true);
auth()
->user()
->setCompany($this->company);
}
protected function getCsvData( $entity_type ) {
$base64_encoded_csv = Cache::pull( $this->hash . '-' . $entity_type );
if ( empty( $base64_encoded_csv ) ) {
protected function getCsvData($entity_type)
{
$base64_encoded_csv = Cache::pull($this->hash . '-' . $entity_type);
if (empty($base64_encoded_csv)) {
return null;
}
$csv = base64_decode( $base64_encoded_csv );
$csv = Reader::createFromString( $csv );
$csv = base64_decode($base64_encoded_csv);
$csv = Reader::createFromString($csv);
$stmt = new Statement();
$data = iterator_to_array( $stmt->process( $csv ) );
$data = iterator_to_array($stmt->process($csv));
if ( count( $data ) > 0 ) {
if (count($data) > 0) {
$headers = $data[0];
// Remove Invoice Ninja headers
if ( count( $headers ) && count( $data ) > 4 && $this->import_type === 'csv' ) {
if (
count($headers) &&
count($data) > 4 &&
$this->import_type === 'csv'
) {
$first_cell = $headers[0];
if ( strstr( $first_cell, config( 'ninja.app_name' ) ) ) {
array_shift( $data ); // Invoice Ninja...
array_shift( $data ); // <blank line>
array_shift( $data ); // Enitty Type Header
if (strstr($first_cell, config('ninja.app_name'))) {
array_shift($data); // Invoice Ninja...
array_shift($data); // <blank line>
array_shift($data); // Enitty Type Header
}
}
}
@ -90,23 +105,28 @@ class BaseImport {
return $data;
}
public function mapCSVHeaderToKeys( $csvData ) {
$keys = array_shift( $csvData );
public function mapCSVHeaderToKeys($csvData)
{
$keys = array_shift($csvData);
return array_map( function ( $values ) use ( $keys ) {
return array_combine( $keys, $values );
}, $csvData );
return array_map(function ($values) use ($keys) {
return array_combine($keys, $values);
}, $csvData);
}
private function groupInvoices( $csvData, $key ) {
private function groupInvoices($csvData, $key)
{
// Group by invoice.
$grouped = [];
foreach ( $csvData as $line_item ) {
if ( empty( $line_item[ $key ] ) ) {
$this->error_array['invoice'][] = [ 'invoice' => $line_item, 'error' => 'No invoice number' ];
foreach ($csvData as $line_item) {
if (empty($line_item[$key])) {
$this->error_array['invoice'][] = [
'invoice' => $line_item,
'error' => 'No invoice number',
];
} else {
$grouped[ $line_item[ $key ] ][] = $line_item;
$grouped[$line_item[$key]][] = $line_item;
}
}
@ -122,185 +142,241 @@ class BaseImport {
{
$count = 0;
foreach ( $data as $record ) {
try {
$entity = $this->transformer->transform( $record );
foreach ($data as $key => $record) {
try {
/** @var \App\Http\Requests\Request $request */
$request = new $this->request_name();
$entity = $this->transformer->transform($record);
// Pass entity data to request so it can be validated
$request->query = $request->request = new ParameterBag( $entity );
$validator = Validator::make( $entity, $request->rules() );
/** @var \App\Http\Requests\Request $request */
$request = new $this->request_name();
if ( $validator->fails() ) {
$this->error_array[ $entity_type ][] =
[ $entity_type => $record, 'error' => $validator->errors()->all() ];
} else {
$entity =
$this->repository->save(
array_diff_key( $entity, [ 'user_id' => false ] ),
$this->factory_name::create( $this->company->id, $this->getUserIDForRecord( $entity ) ) );
// Pass entity data to request so it can be validated
$request->query = $request->request = new ParameterBag($entity);
$validator = Validator::make($entity, $request->rules());
$entity->saveQuietly();
$count++;
if ($validator->fails()) {
$this->error_array[$entity_type][] = [
$entity_type => $record,
'error' => $validator->errors()->all(),
];
} else {
$entity = $this->repository->save(
array_diff_key($entity, ['user_id' => false]),
$this->factory_name::create(
$this->company->id,
$this->getUserIDForRecord($entity)
)
);
}
} catch ( \Exception $ex ) {
if ( $ex instanceof ImportException ) {
$message = $ex->getMessage();
} else {
report( $ex );
$message = 'Unknown error';
}
$entity->saveQuietly();
$count++;
$this->error_array[ $entity_type ][] = [ $entity_type => $record, 'error' => $message ];
}
return $count;
}
}
} catch (\Exception $ex) {
if ($ex instanceof ImportException) {
$message = $ex->getMessage();
} else {
report($ex);
$message = 'Unknown error';
}
$this->error_array[$entity_type][] = [
$entity_type => $record,
'error' => $message,
];
}
return $count;
}
}
public function ingestInvoices( $invoices ) {
public function ingestInvoices($invoices, $invoice_number_key)
{
$invoice_transformer = $this->transformer;
/** @var PaymentRepository $payment_repository */
$payment_repository = app()->make( PaymentRepository::class );
$payment_repository = app()->make(PaymentRepository::class);
$payment_repository->import_mode = true;
/** @var ClientRepository $client_repository */
$client_repository = app()->make( ClientRepository::class );
$client_repository = app()->make(ClientRepository::class);
$client_repository->import_mode = true;
$invoice_repository = new InvoiceRepository();
$invoice_repository = new InvoiceRepository();
$invoice_repository->import_mode = true;
foreach ( $invoices as $raw_invoice ) {
$invoices = $this->groupInvoices($invoices, $invoice_number_key);
foreach ($invoices as $raw_invoice) {
try {
$invoice_data = $invoice_transformer->transform( $raw_invoice );
$invoice_data['line_items'] = $this->cleanItems( $invoice_data['line_items'] ?? [] );
$invoice_data = $invoice_transformer->transform($raw_invoice);
$invoice_data['line_items'] = $this->cleanItems(
$invoice_data['line_items'] ?? []
);
// If we don't have a client ID, but we do have client data, go ahead and create the client.
if ( empty( $invoice_data['client_id'] ) && ! empty( $invoice_data['client'] ) ) {
$client_data = $invoice_data['client'];
$client_data['user_id'] = $this->getUserIDForRecord( $invoice_data );
if (
empty($invoice_data['client_id']) &&
!empty($invoice_data['client'])
) {
$client_data = $invoice_data['client'];
$client_data['user_id'] = $this->getUserIDForRecord(
$invoice_data
);
$client_repository->save(
$client_data,
$client = ClientFactory::create( $this->company->id, $client_data['user_id'] )
$client = ClientFactory::create(
$this->company->id,
$client_data['user_id']
)
);
$invoice_data['client_id'] = $client->id;
unset( $invoice_data['client'] );
unset($invoice_data['client']);
}
$validator = Validator::make( $invoice_data, ( new StoreInvoiceRequest() )->rules() );
if ( $validator->fails() ) {
$this->error_array['invoice'][] =
[ 'invoice' => $invoice_data, 'error' => $validator->errors()->all() ];
$validator = Validator::make(
$invoice_data,
(new StoreInvoiceRequest())->rules()
);
if ($validator->fails()) {
$this->error_array['invoice'][] = [
'invoice' => $invoice_data,
'error' => $validator->errors()->all(),
];
} else {
$invoice = InvoiceFactory::create( $this->company->id, $this->getUserIDForRecord( $invoice_data ) );
if ( ! empty( $invoice_data['status_id'] ) ) {
$invoice = InvoiceFactory::create(
$this->company->id,
$this->getUserIDForRecord($invoice_data)
);
if (!empty($invoice_data['status_id'])) {
$invoice->status_id = $invoice_data['status_id'];
}
$invoice_repository->save( $invoice_data, $invoice );
$this->addInvoiceToMaps( $invoice );
$invoice_repository->save($invoice_data, $invoice);
// If we're doing a generic CSV import, only import payment data if we're not importing a payment CSV.
// If we're doing a platform-specific import, trust the platform to only return payment info if there's not a separate payment CSV.
if ( $this->import_type !== 'csv' || empty( $this->column_map['payment'] ) ) {
if (
$this->import_type !== 'csv' ||
empty($this->column_map['payment'])
) {
// Check for payment columns
if ( ! empty( $invoice_data['payments'] ) ) {
foreach ( $invoice_data['payments'] as $payment_data ) {
$payment_data['user_id'] = $invoice->user_id;
$payment_data['client_id'] = $invoice->client_id;
$payment_data['invoices'] = [
if (!empty($invoice_data['payments'])) {
foreach (
$invoice_data['payments']
as $payment_data
) {
$payment_data['user_id'] = $invoice->user_id;
$payment_data['client_id'] =
$invoice->client_id;
$payment_data['invoices'] = [
[
'invoice_id' => $invoice->id,
'amount' => $payment_data['amount'] ?? null,
'amount' =>
$payment_data['amount'] ?? null,
],
];
/* Make sure we don't apply any payments to invoices with a Zero Amount*/
if($invoice->amount > 0)
{
if ($invoice->amount > 0) {
$payment_repository->save(
$payment_data,
PaymentFactory::create( $this->company->id, $invoice->user_id, $invoice->client_id )
PaymentFactory::create(
$this->company->id,
$invoice->user_id,
$invoice->client_id
)
);
}
}
}
}
$this->actionInvoiceStatus( $invoice, $invoice_data, $invoice_repository );
$this->actionInvoiceStatus(
$invoice,
$invoice_data,
$invoice_repository
);
}
} catch ( \Exception $ex ) {
if ( $ex instanceof ImportException ) {
} catch (\Exception $ex) {
if ($ex instanceof ImportException) {
$message = $ex->getMessage();
} else {
report( $ex );
report($ex);
$message = 'Unknown error';
}
$this->error_array['invoice'][] = [ 'invoice' => $raw_invoice, 'error' => $message ];
$this->error_array['invoice'][] = [
'invoice' => $raw_invoice,
'error' => $message,
];
}
}
}
private function actionInvoiceStatus(
$invoice,
$invoice_data,
$invoice_repository
) {
if (!empty($invoice_data['archived'])) {
$invoice_repository->archive($invoice);
$invoice->fresh();
}
if (!empty($invoice_data['viewed'])) {
$invoice = $invoice
->service()
->markViewed()
->save();
}
if ($invoice->status_id === Invoice::STATUS_DRAFT) {
} elseif ($invoice->status_id === Invoice::STATUS_SENT) {
$invoice = $invoice
->service()
->markSent()
->save();
} elseif (
$invoice->status_id <= Invoice::STATUS_SENT &&
$invoice->amount > 0
) {
if ($invoice->balance <= 0) {
$invoice->status_id = Invoice::STATUS_PAID;
$invoice->save();
} elseif ($invoice->balance != $invoice->amount) {
$invoice->status_id = Invoice::STATUS_PARTIAL;
$invoice->save();
}
}
return $invoice;
}
protected function getUserIDForRecord($record)
{
if (!empty($record['user_id'])) {
return $this->findUser($record['user_id']);
} else {
return $this->company->owner()->id;
}
}
protected function findUser($user_hash)
{
$user = User::where('account_id', $this->company->account->id)
->where(
\DB::raw('CONCAT_WS(" ", first_name, last_name)'),
'like',
'%' . $user_hash . '%'
)
->first();
protected function getUserIDForRecord( $record ) {
if ( ! empty( $record['user_id'] ) ) {
return $this->findUser( $record['user_id'] );
} else {
return $this->company->owner()->id;
}
}
protected function findUser( $user_hash ) {
$user = User::where( 'account_id', $this->company->account->id )
->where( \DB::raw( 'CONCAT_WS(" ", first_name, last_name)' ), 'like', '%' . $user_hash . '%' )
->first();
if ( $user ) {
return $user->id;
} else {
return $this->company->owner()->id;
}
}
if ($user) {
return $user->id;
} else {
return $this->company->owner()->id;
}
}
}

View File

@ -8,50 +8,70 @@
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Import\Providers;
use App\Factory\ClientFactory;
use App\Factory\ExpenseFactory;
use App\Factory\InvoiceFactory;
use App\Factory\PaymentFactory;
use App\Factory\ProductFactory;
use App\Factory\VendorFactory;
use App\Http\Requests\Client\StoreClientRequest;
use App\Http\Requests\Expense\StoreExpenseRequest;
use App\Http\Requests\Invoice\StoreInvoiceRequest;
use App\Http\Requests\Payment\StorePaymentRequest;
use App\Http\Requests\Product\StoreProductRequest;
use App\Http\Requests\Vendor\StoreVendorRequest;
use App\Import\ImportException;
use App\Import\Providers\BaseImport;
use App\Import\Providers\ImportInterface;
use App\Import\Transformer\Csv\ClientTransformer;
use App\Import\Transformer\Csv\ExpenseTransformer;
use App\Import\Transformer\Csv\InvoiceTransformer;
use App\Import\Transformer\Csv\PaymentTransformer;
use App\Import\Transformer\Csv\ProductTransformer;
use App\Import\Transformer\Csv\VendorTransformer;
use App\Repositories\ClientRepository;
use App\Repositories\ExpenseRepository;
use App\Repositories\InvoiceRepository;
use App\Repositories\PaymentRepository;
use App\Repositories\ProductRepository;
use App\Repositories\VendorRepository;
use Illuminate\Support\Facades\Validator;
use Symfony\Component\HttpFoundation\ParameterBag;
class Csv extends BaseImport implements ImportInterface
{
public array $entity_count = [];
public function import(string $entity)
{
if(in_array($entity, [ 'client', 'product', 'invoice', 'payment', 'vendor', 'expense' ]))
public function import(string $entity)
{
if (
in_array($entity, [
'client',
'product',
'invoice',
'payment',
'vendor',
'expense',
])
) {
$this->{$entity}();
}
//collate any errors
}
private function client()
{
$entity_type = 'client';
$data = $this->getCsvData($entity_type);
$data = $this->preTransform($data, $entity_type);
if(empty($data)){
if (empty($data)) {
$this->entity_count['clients'] = 0;
return;
}
@ -60,7 +80,7 @@ class Csv extends BaseImport implements ImportInterface
$this->repository_name = ClientRepository::class;
$this->factory_name = ClientFactory::class;
$this->repository = app()->make( $this->repository_name );
$this->repository = app()->make($this->repository_name);
$this->repository->import_mode = true;
$this->transformer = new ClientTransformer($this->company);
@ -68,20 +88,17 @@ class Csv extends BaseImport implements ImportInterface
$client_count = $this->ingest($data, $entity_type);
$this->entity_count['clients'] = $client_count;
}
private function product()
{
$entity_type = 'product';
$data = $this->getCsvData($entity_type);
$data = $this->preTransform($data, $entity_type);
if(empty($data)){
if (empty($data)) {
$this->entity_count['products'] = 0;
return;
}
@ -90,7 +107,7 @@ class Csv extends BaseImport implements ImportInterface
$this->repository_name = ProductRepository::class;
$this->factory_name = ProductFactory::class;
$this->repository = app()->make( $this->repository_name );
$this->repository = app()->make($this->repository_name);
$this->repository->import_mode = true;
$this->transformer = new ProductTransformer($this->company);
@ -98,20 +115,17 @@ class Csv extends BaseImport implements ImportInterface
$product_count = $this->ingest($data, $entity_type);
$this->entity_count['products'] = $product_count;
}
private function invoice()
{
$entity_type = 'invoice';
$data = $this->getCsvData($entity_type);
$data = $this->preTransform($data, $entity_type);
if(empty($data)){
if (empty($data)) {
$this->entity_count['invoices'] = 0;
return;
}
@ -120,47 +134,127 @@ class Csv extends BaseImport implements ImportInterface
$this->repository_name = InvoiceRepository::class;
$this->factory_name = InvoiceFactory::class;
$this->repository = app()->make( $this->repository_name );
$this->repository = app()->make($this->repository_name);
$this->repository->import_mode = true;
$this->transformer = new ProductTransformer($this->company);
$this->transformer = new InvoiceTransformer($this->company);
$invoice_count = $this->ingest($data, $entity_type);
$invoice_count = $this->ingestInvoices($data, 'invoice.number');
$this->entity_count['invoices'] = $invoice_count;
}
private function payment()
{
$entity_type = 'payment';
public function preTransform(array $data, $entity_type)
{
$data = $this->getCsvData($entity_type);
$data = $this->preTransform($data, $entity_type);
if ( empty( $this->column_map[ $entity_type ] ) ) {
if (empty($data)) {
$this->entity_count['payments'] = 0;
return;
}
$this->request_name = StorePaymentRequest::class;
$this->repository_name = PaymentRepository::class;
$this->factory_name = PaymentFactory::class;
$this->repository = app()->make($this->repository_name);
$this->repository->import_mode = true;
$this->transformer = new PaymentTransformer($this->company);
$payment_count = $this->ingest($data, $entity_type);
$this->entity_count['payments'] = $payment_count;
}
private function vendor()
{
$entity_type = 'vendor';
$data = $this->getCsvData($entity_type);
$data = $this->preTransform($data, $entity_type);
if (empty($data)) {
$this->entity_count['vendors'] = 0;
return;
}
$this->request_name = StoreVendorRequest::class;
$this->repository_name = VendorRepository::class;
$this->factory_name = VendorFactory::class;
$this->repository = app()->make($this->repository_name);
$this->repository->import_mode = true;
$this->transformer = new VendorTransformer($this->company);
$vendor_count = $this->ingest($data, $entity_type);
$this->entity_count['vendors'] = $vendor_count;
}
private function expense()
{
$entity_type = 'expense';
$data = $this->getCsvData($entity_type);
$data = $this->preTransform($data, $entity_type);
if (empty($data)) {
$this->entity_count['expenses'] = 0;
return;
}
$this->request_name = StoreExpenseRequest::class;
$this->repository_name = ExpenseRepository::class;
$this->factory_name = ExpenseFactory::class;
$this->repository = app()->make($this->repository_name);
$this->repository->import_mode = true;
$this->transformer = new ExpenseTransformer($this->company);
$expense_count = $this->ingest($data, $entity_type);
$this->entity_count['expenses'] = $expense_count;
}
private function quote()
{
}
private function task()
{
}
public function preTransform(array $data, $entity_type)
{
if (empty($this->column_map[$entity_type])) {
return false;
}
if ( $this->skip_header ) {
array_shift( $data );
if ($this->skip_header) {
array_shift($data);
}
//sort the array by key
$keys = $this->column_map[ $entity_type ];
ksort( $keys );
$data = array_map( function ( $row ) use ( $keys ) {
return array_combine( $keys, array_intersect_key( $row, $keys ) );
}, $data );
$keys = $this->column_map[$entity_type];
ksort($keys);
$data = array_map(function ($row) use ($keys) {
return array_combine($keys, array_intersect_key($row, $keys));
}, $data);
return $data;
}
public function transform(array $data)
{
public function transform(array $data)
{
}
}
}

View File

@ -12,5 +12,4 @@ namespace App\Import\Providers;
class Freshbooks extends BaseImport
{
}
}

View File

@ -11,6 +11,7 @@
namespace App\Import\Transformer;
use App\Factory\ProjectFactory;
use App\Models\ClientContact;
use App\Models\Country;
use App\Models\PaymentType;
@ -25,7 +26,6 @@ use Illuminate\Support\Facades\Cache;
*/
class BaseTransformer
{
protected $company;
public function __construct($company)
@ -35,55 +35,64 @@ class BaseTransformer
public function getString($data, $field)
{
return (isset($data[$field]) && $data[$field]) ? $data[$field] : '';
return isset($data[$field]) && $data[$field] ? $data[$field] : '';
}
public function getCurrencyByCode( $data, $key = 'client.currency_id' )
public function getCurrencyByCode($data, $key = 'client.currency_id')
{
$code = array_key_exists( $key, $data ) ? $data[ $key ] : false;
$code = array_key_exists($key, $data) ? $data[$key] : false;
$currencies = Cache::get('currencies');
$currency = $currencies->filter(function ($item) use($code) {
return $item->code == $code;
})->first();
return $currency ? $currency->id : $this->company->settings->currency_id;
$currency = $currencies
->filter(function ($item) use ($code) {
return $item->code == $code;
})
->first();
return $currency
? $currency->id
: $this->company->settings->currency_id;
}
public function getClient($client_name, $client_email) {
public function getClient($client_name, $client_email)
{
// nlog("searching for {$client_name} with email {$client_email}");
$client_id_search = $this->company->clients()->where( 'id_number', $client_name );
$client_id_search = $this->company
->clients()
->where('id_number', $client_name);
if ( $client_id_search->count() >= 1 ) {
return $client_id_search->first()->id;
nlog("found via id number");
}
$client_name_search = $this->company->clients()->where( 'name', $client_name );
if ( $client_name_search->count() >= 1 ) {
return $client_name_search->first()->id;
nlog("found via name");
if ($client_id_search->count() >= 1) {
// nlog("found via id number => {$client_id_search->first()->id}");
return $client_id_search->first()->id;
}
if ( ! empty( $client_email ) ) {
$contacts = ClientContact::where( 'company_id', $this->company->id )
->where( 'email', $client_email );
$client_name_search = $this->company
->clients()
->where('name', $client_name);
if ( $contacts->count() >= 1 ) {
return $contacts->first()->client_id;
nlog("found via contact");
}
}
nlog("did not find client");
if ($client_name_search->count() >= 1) {
// nlog("found via name {$client_name_search->first()->id}");
return $client_name_search->first()->id;
}
return null;
}
if (!empty($client_email)) {
$contacts = ClientContact::where(
'company_id',
$this->company->id
)->where('email', $client_email);
if ($contacts->count() >= 1) {
// nlog("found via contact {$contacts->first()->client_id}");
return $contacts->first()->client_id;
}
}
// nlog("did not find client");
return null;
}
///////////////////////////////////////////////////////////////////////////////////
/**
@ -93,7 +102,12 @@ class BaseTransformer
*/
public function hasClient($name)
{
return $this->company->clients()->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->exists();
return $this->company
->clients()
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
strtolower(str_replace(' ', '', $name)),
])
->exists();
}
/**
@ -103,7 +117,27 @@ class BaseTransformer
*/
public function hasVendor($name)
{
return $this->company->vendors()->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->exists();
return $this->company
->vendors()
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
strtolower(str_replace(' ', '', $name)),
])
->exists();
}
/**
* @param $name
*
* @return bool
*/
public function hasProject($name)
{
return $this->company
->projects()
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
strtolower(str_replace(' ', '', $name)),
])
->exists();
}
/**
@ -113,7 +147,12 @@ class BaseTransformer
*/
public function hasProduct($key)
{
return $this->company->products()->whereRaw("LOWER(REPLACE(`product_key`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $key))])->exists();
return $this->company
->products()
->whereRaw("LOWER(REPLACE(`product_key`, ' ' ,'')) = ?", [
strtolower(str_replace(' ', '', $key)),
])
->exists();
}
/**
@ -140,7 +179,12 @@ class BaseTransformer
*/
public function getClientId($name)
{
$client = $this->company->clients()->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->first();
$client = $this->company
->clients()
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
strtolower(str_replace(' ', '', $name)),
])
->first();
return $client ? $client->id : null;
}
@ -152,14 +196,18 @@ class BaseTransformer
*/
public function getProduct($data, $key, $field, $default = false)
{
$product = $this->company
->products()
->whereRaw("LOWER(REPLACE(`product_key`, ' ' ,'')) = ?", [
strtolower(str_replace(' ', '', $data->{$key})),
])
->first();
$product = $this->company->products()->whereRaw("LOWER(REPLACE(`product_key`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $data->{$key}))])->first();
if($product)
if ($product) {
return $product->{$field} ?: $default;
}
return $default;
}
/**
@ -169,14 +217,18 @@ class BaseTransformer
*/
public function getContact($email)
{
$contact = $this->company->client_contacts()->whereRaw("LOWER(REPLACE(`email`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $email))])->first();
$contact = $this->company
->client_contacts()
->whereRaw("LOWER(REPLACE(`email`, ' ' ,'')) = ?", [
strtolower(str_replace(' ', '', $email)),
])
->first();
if(!$contact)
if (!$contact) {
return null;
}
return $contact;
}
/**
@ -186,10 +238,13 @@ class BaseTransformer
*/
public function getCountryId($name)
{
if(strlen($name) == 2)
if (strlen($name) == 2) {
return $this->getCountryIdBy2($name);
}
$country = Country::whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->first();
$country = Country::whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
strtolower(str_replace(' ', '', $name)),
])->first();
return $country ? $country->id : null;
}
@ -201,7 +256,9 @@ class BaseTransformer
*/
public function getCountryIdBy2($name)
{
return Country::where('iso_3166_2', $name)->exists() ? Country::where('iso_3166_2', $name)->first()->id : null;
return Country::where('iso_3166_2', $name)->exists()
? Country::where('iso_3166_2', $name)->first()->id
: null;
}
/**
@ -213,7 +270,12 @@ class BaseTransformer
{
$name = strtolower(trim($name));
$tax_rate = $this->company->tax_rates()->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->first();
$tax_rate = $this->company
->tax_rates()
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
strtolower(str_replace(' ', '', $name)),
])
->first();
return $tax_rate ? $tax_rate->rate : 0;
}
@ -227,10 +289,14 @@ class BaseTransformer
{
$name = strtolower(trim($name));
$tax_rate = $this->company->tax_rates()->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->first();
$tax_rate = $this->company
->tax_rates()
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
strtolower(str_replace(' ', '', $name)),
])
->first();
return $tax_rate ? $tax_rate->name : '';
}
/**
@ -262,7 +328,7 @@ class BaseTransformer
*/
public function getInvoiceNumber($number)
{
return $number ? ltrim( trim( $number ), '0' ) : null;
return $number ? ltrim(trim($number), '0') : null;
}
/**
@ -272,9 +338,14 @@ class BaseTransformer
*/
public function getInvoiceId($invoice_number)
{
$invoice = $this->company->invoices()->whereRaw("LOWER(REPLACE(`number`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $invoice_number))])->first();
$invoice = $this->company
->invoices()
->whereRaw("LOWER(REPLACE(`number`, ' ' ,'')) = ?", [
strtolower(str_replace(' ', '', $invoice_number)),
])
->first();
return $invoice ? $invoice->id : null;
return $invoice ? $invoice->id : null;
}
/**
@ -284,9 +355,12 @@ class BaseTransformer
*/
public function hasInvoice($invoice_number)
{
return $this->company->invoices()->whereRaw("LOWER(REPLACE(`number`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $invoice_number))])->exists();
return $this->company
->invoices()
->whereRaw("LOWER(REPLACE(`number`, ' ' ,'')) = ?", [
strtolower(str_replace(' ', '', $invoice_number)),
])
->exists();
}
/**
@ -296,7 +370,12 @@ class BaseTransformer
*/
public function getInvoiceClientId($invoice_number)
{
$invoice = $this->company->invoices()->whereRaw("LOWER(REPLACE(`number`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $invoice_number))])->first();
$invoice = $this->company
->invoices()
->whereRaw("LOWER(REPLACE(`number`, ' ' ,'')) = ?", [
strtolower(str_replace(' ', '', $invoice_number)),
])
->first();
return $invoice ? $invoice->client_id : null;
}
@ -308,47 +387,75 @@ class BaseTransformer
*/
public function getVendorId($name)
{
$vendor = $this->company->vendors()->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->first();
$vendor = $this->company
->vendors()
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
strtolower(str_replace(' ', '', $name)),
])
->first();
return $vendor ? $vendor->id : null;
}
/**
* @param $name
*
* @return int|null
*/
public function getExpenseCategoryId( $name ) {
$ec = $this->company->expense_categories()->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->first();
/**
* @param $name
*
* @return int|null
*/
public function getExpenseCategoryId($name)
{
$ec = $this->company
->expense_categories()
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
strtolower(str_replace(' ', '', $name)),
])
->first();
return $ec ? $ec->id : null;
}
}
/**
* @param $name
*
* @return int|null
*/
public function getProjectId($name, $clientId = null)
{
$project = $this->company
->projects()
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
strtolower(str_replace(' ', '', $name)),
])
->first();
/**
* @param $name
*
* @return int|null
*/
public function getProjectId( $name ) {
return $project ? $project->id : $this->createProject($name, $clientId);
}
$project = $this->company->projects()->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->first();
private function createProject($name, $clientId)
{
$project = ProjectFactory::create($this->company->id, $this->company->owner()->id);
$project->name = $name;
return $project ? $project->id : null;
}
if($clientId)
$project->client_id = $clientId;
$project->saveQuietly();
/**
* @param $name
*
* @return int|null
*/
public function getPaymentTypeId( $name ) {
$pt = PaymentType::whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->first();
return $project->id;
}
/**
* @param $name
*
* @return int|null
*/
public function getPaymentTypeId($name)
{
$pt = PaymentType::whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
strtolower(str_replace(' ', '', $name)),
])->first();
return $pt ? $pt->id : null;
}
}
}

View File

@ -19,64 +19,101 @@ use Illuminate\Support\Str;
*/
class ClientTransformer extends BaseTransformer
{
/**
/**
* @param $data
*
* @return array|bool
*/
public function transform($data)
{
if (isset($data->name) && $this->hasClient($data->name)) {
throw new ImportException('Client already exists');
}
public function transform($data)
{
if (isset($data->name) && $this->hasClient($data->name)) {
throw new ImportException('Client already exists');
}
$settings = new \stdClass;
$settings->currency_id = (string)$this->getCurrencyByCode($data);
$settings = new \stdClass();
$settings->currency_id = (string) $this->getCurrencyByCode($data);
return [
'company_id' => $this->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' ),
'postal_code' => $this->getString( $data, 'client.postal_code'),
'city' => $this->getString( $data, 'client.city' ),
'state' => $this->getString( $data, 'client.state' ),
'shipping_address1' => $this->getString( $data, 'client.shipping_address1' ),
'shipping_address2' => $this->getString( $data, 'client.shipping_address2' ),
'shipping_city' => $this->getString( $data, 'client.shipping_city' ),
'shipping_state' => $this->getString( $data, 'client.shipping_state' ),
'shipping_postal_code' => $this->getString( $data, 'client.shipping_postal_code' ),
'public_notes' => $this->getString( $data, 'client.public_notes' ),
'private_notes' => $this->getString( $data, 'client.private_notes' ),
'website' => $this->getString( $data, 'client.website' ),
'vat_number' => $this->getString( $data, 'client.vat_number' ),
'id_number' => $this->getString( $data, 'client.id_number' ),
'custom_value1' => $this->getString( $data, 'client.custom_value1' ),
'custom_value2' => $this->getString( $data, 'client.custom_value2' ),
'custom_value3' => $this->getString( $data, 'client.custom_value3' ),
'custom_value4' => $this->getString( $data, 'client.custom_value4' ),
'balance' => preg_replace( '/[^0-9,.]+/', '', $this->getFloat( $data, 'client.balance' ) ),
'paid_to_date' => preg_replace( '/[^0-9,.]+/', '', $this->getFloat( $data, 'client.paid_to_date' ) ),
'credit_balance' => 0,
'settings' => $settings,
'client_hash' => Str::random( 40 ),
'contacts' => [
'company_id' => $this->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'),
'postal_code' => $this->getString($data, 'client.postal_code'),
'city' => $this->getString($data, 'client.city'),
'state' => $this->getString($data, 'client.state'),
'shipping_address1' => $this->getString(
$data,
'client.shipping_address1'
),
'shipping_address2' => $this->getString(
$data,
'client.shipping_address2'
),
'shipping_city' => $this->getString($data, 'client.shipping_city'),
'shipping_state' => $this->getString(
$data,
'client.shipping_state'
),
'shipping_postal_code' => $this->getString(
$data,
'client.shipping_postal_code'
),
'public_notes' => $this->getString($data, 'client.public_notes'),
'private_notes' => $this->getString($data, 'client.private_notes'),
'website' => $this->getString($data, 'client.website'),
'vat_number' => $this->getString($data, 'client.vat_number'),
'id_number' => $this->getString($data, 'client.id_number'),
'custom_value1' => $this->getString($data, 'client.custom_value1'),
'custom_value2' => $this->getString($data, 'client.custom_value2'),
'custom_value3' => $this->getString($data, 'client.custom_value3'),
'custom_value4' => $this->getString($data, 'client.custom_value4'),
'balance' => preg_replace(
'/[^0-9,.]+/',
'',
$this->getFloat($data, 'client.balance')
),
'paid_to_date' => preg_replace(
'/[^0-9,.]+/',
'',
$this->getFloat($data, 'client.paid_to_date')
),
'credit_balance' => 0,
'settings' => $settings,
'client_hash' => Str::random(40),
'contacts' => [
[
'first_name' => $this->getString( $data, 'contact.first_name' ),
'last_name' => $this->getString( $data, 'contact.last_name' ),
'email' => $this->getString( $data, 'contact.email' ),
'phone' => $this->getString( $data, 'contact.phone' ),
'custom_value1' => $this->getString( $data, 'contact.custom_value1' ),
'custom_value2' => $this->getString( $data, 'contact.custom_value2' ),
'custom_value3' => $this->getString( $data, 'contact.custom_value3' ),
'custom_value4' => $this->getString( $data, 'contact.custom_value4' ),
'first_name' => $this->getString(
$data,
'contact.first_name'
),
'last_name' => $this->getString($data, 'contact.last_name'),
'email' => $this->getString($data, 'contact.email'),
'phone' => $this->getString($data, 'contact.phone'),
'custom_value1' => $this->getString(
$data,
'contact.custom_value1'
),
'custom_value2' => $this->getString(
$data,
'contact.custom_value2'
),
'custom_value3' => $this->getString(
$data,
'contact.custom_value3'
),
'custom_value4' => $this->getString(
$data,
'contact.custom_value4'
),
],
],
'country_id' => isset( $data['client.country'] ) ? $this->getCountryId( $data['client.country']) : null,
'shipping_country_id' => isset($data['client.shipping_country'] ) ? $this->getCountryId( $data['client.shipping_country'] ) : null,
'country_id' => isset($data['client.country'])
? $this->getCountryId($data['client.country'])
: null,
'shipping_country_id' => isset($data['client.shipping_country'])
? $this->getCountryId($data['client.shipping_country'])
: null,
];
}
}
}

View File

@ -0,0 +1,70 @@
<?php
/**
* client Ninja (https://clientninja.com).
*
* @link https://github.com/clientninja/clientninja source repository
*
* @copyright Copyright (c) 2021. client Ninja LLC (https://clientninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Import\Transformer\Csv;
use App\Import\Transformer\BaseTransformer;
/**
* Class InvoiceTransformer.
*/
class ExpenseTransformer extends BaseTransformer
{
/**
* @param $data
*
* @return bool|array
*/
public function transform($data)
{
$clientId = isset($data['expense.client'])
? $this->getClientId($data['expense.client'])
: null;
return [
'company_id' => $this->company->id,
'amount' => $this->getFloat($data, 'expense.amount'),
'currency_id' => $this->getCurrencyByCode(
$data,
'expense.currency_id'
),
'vendor_id' => isset($data['expense.vendor'])
? $this->getVendorId($data['expense.vendor'])
: null,
'client_id' => isset($data['expense.client'])
? $this->getClientId($data['expense.client'])
: null,
'date' => $clientId,
'public_notes' => $this->getString($data, 'expense.public_notes'),
'private_notes' => $this->getString($data, 'expense.private_notes'),
'category_id' => isset($data['expense.category'])
? $this->getExpenseCategoryId($data['expense.category'])
: null,
'project_id' => isset($data['expense.project'])
? $this->getProjectId($data['expense.project'], $clientId)
: null,
'payment_type_id' => isset($data['expense.payment_type'])
? $this->getPaymentTypeId($data['expense.payment_type'])
: null,
'payment_date' => isset($data['expense.payment_date'])
? date('Y-m-d', strtotime($data['expense.payment_date']))
: null,
'custom_value1' => $this->getString($data, 'expense.custom_value1'),
'custom_value2' => $this->getString($data, 'expense.custom_value2'),
'custom_value3' => $this->getString($data, 'expense.custom_value3'),
'custom_value4' => $this->getString($data, 'expense.custom_value4'),
'transaction_reference' => $this->getString(
$data,
'expense.transaction_reference'
),
'should_be_invoiced' => $clientId ? true : false,
];
}
}

View File

@ -13,113 +13,227 @@ namespace App\Import\Transformer\Csv;
use App\Import\ImportException;
use App\Import\Transformer\BaseTransformer;
use App\Import\Transformer\Csv\ClientTransformer;
use App\Models\Invoice;
/**
* Class InvoiceTransformer.
*/
class InvoiceTransformer extends BaseTransformer {
class InvoiceTransformer extends BaseTransformer
{
/**
* @param $data
*
* @return bool|array
*/
public function transform( $line_items_data ) {
$invoice_data = reset( $line_items_data );
public function transform($line_items_data)
{
$invoice_data = reset($line_items_data);
if ( $this->hasInvoice( $invoice_data['invoice.number'] ) ) {
throw new ImportException( 'Invoice number already exists' );
if ($this->hasInvoice($invoice_data['invoice.number'])) {
throw new ImportException('Invoice number already exists');
}
$invoiceStatusMap = [
'sent' => Invoice::STATUS_SENT,
'sent' => Invoice::STATUS_SENT,
'draft' => Invoice::STATUS_DRAFT,
];
$transformed = [
'company_id' => $this->company->id,
'number' => $this->getString( $invoice_data, 'invoice.number' ),
'user_id' => $this->getString( $invoice_data, 'invoice.user_id' ),
'amount' => $amount = $this->getFloat( $invoice_data, 'invoice.amount' ),
'balance' => isset( $invoice_data['invoice.balance'] ) ? $this->getFloat( $invoice_data, 'invoice.balance' ) : $amount,
'client_id' => $this->getClient( $this->getString( $invoice_data, 'client.name' ), $this->getString( $invoice_data, 'client.email' ) ),
'discount' => $this->getFloat( $invoice_data, 'invoice.discount' ),
'po_number' => $this->getString( $invoice_data, 'invoice.po_number' ),
'date' => isset( $invoice_data['invoice.date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['invoice.date'] ) ) : now()->format('Y-m-d'),
'due_date' => isset( $invoice_data['invoice.due_date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['invoice.due_date'] ) ) : null,
'terms' => $this->getString( $invoice_data, 'invoice.terms' ),
'public_notes' => $this->getString( $invoice_data, 'invoice.public_notes' ),
'private_notes' => $this->getString( $invoice_data, 'invoice.private_notes' ),
'tax_name1' => $this->getString( $invoice_data, 'invoice.tax_name1' ),
'tax_rate1' => $this->getFloat( $invoice_data, 'invoice.tax_rate1' ),
'tax_name2' => $this->getString( $invoice_data, 'invoice.tax_name2' ),
'tax_rate2' => $this->getFloat( $invoice_data, 'invoice.tax_rate2' ),
'tax_name3' => $this->getString( $invoice_data, 'invoice.tax_name3' ),
'tax_rate3' => $this->getFloat( $invoice_data, 'invoice.tax_rate3' ),
'custom_value1' => $this->getString( $invoice_data, 'invoice.custom_value1' ),
'custom_value2' => $this->getString( $invoice_data, 'invoice.custom_value2' ),
'custom_value3' => $this->getString( $invoice_data, 'invoice.custom_value3' ),
'custom_value4' => $this->getString( $invoice_data, 'invoice.custom_value4' ),
'footer' => $this->getString( $invoice_data, 'invoice.footer' ),
'partial' => $this->getFloat( $invoice_data, 'invoice.partial' ),
'partial_due_date' => $this->getString( $invoice_data, 'invoice.partial_due_date' ),
'custom_surcharge1' => $this->getString( $invoice_data, 'invoice.custom_surcharge1' ),
'custom_surcharge2' => $this->getString( $invoice_data, 'invoice.custom_surcharge2' ),
'custom_surcharge3' => $this->getString( $invoice_data, 'invoice.custom_surcharge3' ),
'custom_surcharge4' => $this->getString( $invoice_data, 'invoice.custom_surcharge4' ),
'exchange_rate' => $this->getString( $invoice_data, 'invoice.exchange_rate' ),
'status_id' => $invoiceStatusMap[ $status =
strtolower( $this->getString( $invoice_data, 'invoice.status' ) ) ] ??
Invoice::STATUS_SENT,
'archived' => $status === 'archived',
'company_id' => $this->company->id,
'number' => $this->getString($invoice_data, 'invoice.number'),
'user_id' => $this->getString($invoice_data, 'invoice.user_id'),
'amount' => ($amount = $this->getFloat(
$invoice_data,
'invoice.amount'
)),
'balance' => isset($invoice_data['invoice.balance'])
? $this->getFloat($invoice_data, 'invoice.balance')
: $amount,
'client_id' => $this->getClient(
$this->getString($invoice_data, 'client.name'),
$this->getString($invoice_data, 'client.email')
),
'discount' => $this->getFloat($invoice_data, 'invoice.discount'),
'po_number' => $this->getString($invoice_data, 'invoice.po_number'),
'date' => isset($invoice_data['invoice.date'])
? date('Y-m-d', strtotime($invoice_data['invoice.date']))
: now()->format('Y-m-d'),
'due_date' => isset($invoice_data['invoice.due_date'])
? date('Y-m-d', strtotime($invoice_data['invoice.due_date']))
: null,
'terms' => $this->getString($invoice_data, 'invoice.terms'),
'public_notes' => $this->getString(
$invoice_data,
'invoice.public_notes'
),
'private_notes' => $this->getString(
$invoice_data,
'invoice.private_notes'
),
'tax_name1' => $this->getString($invoice_data, 'invoice.tax_name1'),
'tax_rate1' => $this->getFloat($invoice_data, 'invoice.tax_rate1'),
'tax_name2' => $this->getString($invoice_data, 'invoice.tax_name2'),
'tax_rate2' => $this->getFloat($invoice_data, 'invoice.tax_rate2'),
'tax_name3' => $this->getString($invoice_data, 'invoice.tax_name3'),
'tax_rate3' => $this->getFloat($invoice_data, 'invoice.tax_rate3'),
'custom_value1' => $this->getString(
$invoice_data,
'invoice.custom_value1'
),
'custom_value2' => $this->getString(
$invoice_data,
'invoice.custom_value2'
),
'custom_value3' => $this->getString(
$invoice_data,
'invoice.custom_value3'
),
'custom_value4' => $this->getString(
$invoice_data,
'invoice.custom_value4'
),
'footer' => $this->getString($invoice_data, 'invoice.footer'),
'partial' => $this->getFloat($invoice_data, 'invoice.partial'),
'partial_due_date' => $this->getString(
$invoice_data,
'invoice.partial_due_date'
),
'custom_surcharge1' => $this->getString(
$invoice_data,
'invoice.custom_surcharge1'
),
'custom_surcharge2' => $this->getString(
$invoice_data,
'invoice.custom_surcharge2'
),
'custom_surcharge3' => $this->getString(
$invoice_data,
'invoice.custom_surcharge3'
),
'custom_surcharge4' => $this->getString(
$invoice_data,
'invoice.custom_surcharge4'
),
'exchange_rate' => $this->getString(
$invoice_data,
'invoice.exchange_rate'
),
'status_id' =>
$invoiceStatusMap[
($status = strtolower(
$this->getString($invoice_data, 'invoice.status')
))
] ?? Invoice::STATUS_SENT,
'archived' => $status === 'archived',
];
if ( isset( $invoice_data['payment.amount'] ) ) {
/* If we can't find the client, then lets try and create a client */
if (!$transformed['client_id']) {
$client_transformer = new ClientTransformer($this->company);
$transformed['client'] = $client_transformer->transform(
$invoice_data
);
}
if (isset($invoice_data['payment.amount'])) {
$transformed['payments'] = [
[
'date' => isset( $invoice_data['payment.date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['payment.date'] ) ) : date( 'y-m-d' ),
'transaction_reference' => $this->getString( $invoice_data, 'payment.transaction_reference' ),
'amount' => $this->getFloat( $invoice_data, 'payment.amount' ),
'date' => isset($invoice_data['payment.date'])
? date(
'Y-m-d',
strtotime($invoice_data['payment.date'])
)
: date('y-m-d'),
'transaction_reference' => $this->getString(
$invoice_data,
'payment.transaction_reference'
),
'amount' => $this->getFloat(
$invoice_data,
'payment.amount'
),
],
];
} elseif ( $status === 'paid' ) {
} elseif ($status === 'paid') {
$transformed['payments'] = [
[
'date' => isset( $invoice_data['payment.date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['payment.date'] ) ) : date( 'y-m-d' ),
'transaction_reference' => $this->getString( $invoice_data, 'payment.transaction_reference' ),
'amount' => $this->getFloat( $invoice_data, 'invoice.amount' ),
'date' => isset($invoice_data['payment.date'])
? date(
'Y-m-d',
strtotime($invoice_data['payment.date'])
)
: date('y-m-d'),
'transaction_reference' => $this->getString(
$invoice_data,
'payment.transaction_reference'
),
'amount' => $this->getFloat(
$invoice_data,
'invoice.amount'
),
],
];
} elseif ( isset( $transformed['amount'] ) && isset( $transformed['balance'] ) && ($transformed['amount'] != $transformed['balance'])) {
} elseif (
isset($transformed['amount']) &&
isset($transformed['balance']) &&
$transformed['amount'] != $transformed['balance']
) {
$transformed['payments'] = [
[
'date' => isset( $invoice_data['payment.date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['payment.date'] ) ) : date( 'y-m-d' ),
'transaction_reference' => $this->getString( $invoice_data, 'payment.transaction_reference' ),
'amount' => $transformed['amount'] - $transformed['balance'],
'date' => isset($invoice_data['payment.date'])
? date(
'Y-m-d',
strtotime($invoice_data['payment.date'])
)
: date('y-m-d'),
'transaction_reference' => $this->getString(
$invoice_data,
'payment.transaction_reference'
),
'amount' =>
$transformed['amount'] - $transformed['balance'],
],
];
}
$line_items = [];
foreach ( $line_items_data as $record ) {
foreach ($line_items_data as $record) {
$line_items[] = [
'quantity' => $this->getFloat( $record, 'item.quantity' ),
'cost' => $this->getFloat( $record, 'item.cost' ),
'product_key' => $this->getString( $record, 'item.product_key' ),
'notes' => $this->getString( $record, 'item.notes' ),
'discount' => $this->getFloat( $record, 'item.discount' ),
'is_amount_discount' => filter_var( $this->getString( $record, 'item.is_amount_discount' ), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE ),
'tax_name1' => $this->getString( $record, 'item.tax_name1' ),
'tax_rate1' => $this->getFloat( $record, 'item.tax_rate1' ),
'tax_name2' => $this->getString( $record, 'item.tax_name2' ),
'tax_rate2' => $this->getFloat( $record, 'item.tax_rate2' ),
'tax_name3' => $this->getString( $record, 'item.tax_name3' ),
'tax_rate3' => $this->getFloat( $record, 'item.tax_rate3' ),
'custom_value1' => $this->getString( $record, 'item.custom_value1' ),
'custom_value2' => $this->getString( $record, 'item.custom_value2' ),
'custom_value3' => $this->getString( $record, 'item.custom_value3' ),
'custom_value4' => $this->getString( $record, 'item.custom_value4' ),
'type_id' => $this->getInvoiceTypeId( $record, 'item.type_id' ),
'quantity' => $this->getFloat($record, 'item.quantity'),
'cost' => $this->getFloat($record, 'item.cost'),
'product_key' => $this->getString($record, 'item.product_key'),
'notes' => $this->getString($record, 'item.notes'),
'discount' => $this->getFloat($record, 'item.discount'),
'is_amount_discount' => filter_var(
$this->getString($record, 'item.is_amount_discount'),
FILTER_VALIDATE_BOOLEAN,
FILTER_NULL_ON_FAILURE
),
'tax_name1' => $this->getString($record, 'item.tax_name1'),
'tax_rate1' => $this->getFloat($record, 'item.tax_rate1'),
'tax_name2' => $this->getString($record, 'item.tax_name2'),
'tax_rate2' => $this->getFloat($record, 'item.tax_rate2'),
'tax_name3' => $this->getString($record, 'item.tax_name3'),
'tax_rate3' => $this->getFloat($record, 'item.tax_rate3'),
'custom_value1' => $this->getString(
$record,
'item.custom_value1'
),
'custom_value2' => $this->getString(
$record,
'item.custom_value2'
),
'custom_value3' => $this->getString(
$record,
'item.custom_value3'
),
'custom_value4' => $this->getString(
$record,
'item.custom_value4'
),
'type_id' => '1', //$this->getInvoiceTypeId( $record, 'item.type_id' ),
];
}
$transformed['line_items'] = $line_items;

View File

@ -0,0 +1,72 @@
<?php
/**
* client Ninja (https://clientninja.com).
*
* @link https://github.com/clientninja/clientninja source repository
*
* @copyright Copyright (c) 2021. client Ninja LLC (https://clientninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Import\Transformer\Csv;
use App\Import\ImportException;
use App\Import\Transformer\BaseTransformer;
/**
* Class PaymentTransformer.
*/
class PaymentTransformer extends BaseTransformer
{
/**
* @param $data
*
* @return array
*/
public function transform($data)
{
$client_id = $this->getClient(
$this->getString($data, 'payment.client_id'),
$this->getString($data, 'payment.client_id')
);
if (empty($client_id)) {
throw new ImportException('Could not find client.');
}
$transformed = [
'company_id' => $this->maps['company']->id,
'number' => $this->getString($data, 'payment.number'),
'user_id' => $this->getString($data, 'payment.user_id'),
'amount' => $this->getFloat($data, 'payment.amount'),
'refunded' => $this->getFloat($data, 'payment.refunded'),
'applied' => $this->getFloat($data, 'payment.applied'),
'transaction_reference' => $this->getString(
$data,
'payment.transaction_reference '
),
'date' => $this->getString($data, 'payment.date'),
'private_notes' => $this->getString($data, 'payment.private_notes'),
'custom_value1' => $this->getString($data, 'payment.custom_value1'),
'custom_value2' => $this->getString($data, 'payment.custom_value2'),
'custom_value3' => $this->getString($data, 'payment.custom_value3'),
'custom_value4' => $this->getString($data, 'payment.custom_value4'),
'client_id' => $client_id,
];
if (
isset($data['payment.invoice_number']) &&
($invoice_id = $this->getInvoiceId($data['payment.invoice_number']))
) {
$transformed['invoices'] = [
[
'invoice_id' => $invoice_id,
'amount' => $transformed['amount'] ?? null,
],
];
}
return $transformed;
}
}

View File

@ -0,0 +1,70 @@
<?php
/**
* client Ninja (https://clientninja.com).
*
* @link https://github.com/clientninja/clientninja source repository
*
* @copyright Copyright (c) 2021. client Ninja LLC (https://clientninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Import\Transformer\Csv;
use App\Import\ImportException;
use App\Import\Transformer\BaseTransformer;
/**
* Class VendorTransformer.
*/
class VendorTransformer extends BaseTransformer
{
/**
* @param $data
*
* @return array|bool
*/
public function transform($data)
{
if (isset($data->name) && $this->hasVendor($data->name)) {
throw new ImportException('Vendor already exists');
}
return [
'company_id' => $this->company->id,
'name' => $this->getString($data, 'vendor.name'),
'phone' => $this->getString($data, 'vendor.phone'),
'id_number' => $this->getString($data, 'vendor.id_number'),
'vat_number' => $this->getString($data, 'vendor.vat_number'),
'website' => $this->getString($data, 'vendor.website'),
'currency_id' => $this->getCurrencyByCode(
$data,
'vendor.currency_id'
),
'public_notes' => $this->getString($data, 'vendor.public_notes'),
'private_notes' => $this->getString($data, 'vendor.private_notes'),
'address1' => $this->getString($data, 'vendor.address1'),
'address2' => $this->getString($data, 'vendor.address2'),
'city' => $this->getString($data, 'vendor.city'),
'state' => $this->getString($data, 'vendor.state'),
'postal_code' => $this->getString($data, 'vendor.postal_code'),
'custom_value1' => $this->getString($data, 'vendor.custom_value1'),
'custom_value2' => $this->getString($data, 'vendor.custom_value2'),
'custom_value3' => $this->getString($data, 'vendor.custom_value3'),
'custom_value4' => $this->getString($data, 'vendor.custom_value4'),
'vendor_contacts' => [
[
'first_name' => $this->getString(
$data,
'vendor.first_name'
),
'last_name' => $this->getString($data, 'vendor.last_name'),
'email' => $this->getString($data, 'vendor.email'),
'phone' => $this->getString($data, 'vendor.phone'),
],
],
'country_id' => isset($data['vendor.country_id'])
? $this->getCountryId($data['vendor.country_id'])
: null,
];
}
}

View File

@ -85,7 +85,7 @@ class BaseTransformer
nlog("found via contact");
}
}
nlog("did not find client");
// nlog("did not find client");
return null;
}

View File

@ -19,62 +19,101 @@ use Illuminate\Support\Str;
*/
class ClientTransformer extends BaseTransformer
{
/**
/**
* @param $data
*
* @return array|bool
*/
public function transform($data)
{
if (isset($data->name) && $this->hasClient($data->name)) {
throw new ImportException('Client already exists');
}
public function transform($data)
{
if (isset($data->name) && $this->hasClient($data->name)) {
throw new ImportException('Client already exists');
}
$settings = new \stdClass;
$settings->currency_id = (string)$this->getCurrencyByCode($data);
$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' ),
'postal_code' => $this->getString( $data, 'client.postal_code'),
'city' => $this->getString( $data, 'client.city' ),
'state' => $this->getString( $data, 'client.state' ),
'shipping_address1' => $this->getString( $data, 'client.shipping_address1' ),
'shipping_address2' => $this->getString( $data, 'client.shipping_address2' ),
'shipping_city' => $this->getString( $data, 'client.shipping_city' ),
'shipping_state' => $this->getString( $data, 'client.shipping_state' ),
'shipping_postal_code' => $this->getString( $data, 'client.shipping_postal_code' ),
'public_notes' => $this->getString( $data, 'client.public_notes' ),
'private_notes' => $this->getString( $data, 'client.private_notes' ),
'website' => $this->getString( $data, 'client.website' ),
'vat_number' => $this->getString( $data, 'client.vat_number' ),
'id_number' => $this->getString( $data, 'client.id_number' ),
'custom_value1' => $this->getString( $data, 'client.custom_value1' ),
'custom_value2' => $this->getString( $data, 'client.custom_value2' ),
'custom_value3' => $this->getString( $data, 'client.custom_value3' ),
'custom_value4' => $this->getString( $data, 'client.custom_value4' ),
'balance' => preg_replace( '/[^0-9,.]+/', '', $this->getFloat( $data, 'client.balance' ) ),
'paid_to_date' => preg_replace( '/[^0-9,.]+/', '', $this->getFloat( $data, 'client.paid_to_date' ) ),
'credit_balance' => 0,
'settings' => $settings,
'client_hash' => Str::random( 40 ),
'contacts' => [
'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'),
'postal_code' => $this->getString($data, 'client.postal_code'),
'city' => $this->getString($data, 'client.city'),
'state' => $this->getString($data, 'client.state'),
'shipping_address1' => $this->getString(
$data,
'client.shipping_address1'
),
'shipping_address2' => $this->getString(
$data,
'client.shipping_address2'
),
'shipping_city' => $this->getString($data, 'client.shipping_city'),
'shipping_state' => $this->getString(
$data,
'client.shipping_state'
),
'shipping_postal_code' => $this->getString(
$data,
'client.shipping_postal_code'
),
'public_notes' => $this->getString($data, 'client.public_notes'),
'private_notes' => $this->getString($data, 'client.private_notes'),
'website' => $this->getString($data, 'client.website'),
'vat_number' => $this->getString($data, 'client.vat_number'),
'id_number' => $this->getString($data, 'client.id_number'),
'custom_value1' => $this->getString($data, 'client.custom_value1'),
'custom_value2' => $this->getString($data, 'client.custom_value2'),
'custom_value3' => $this->getString($data, 'client.custom_value3'),
'custom_value4' => $this->getString($data, 'client.custom_value4'),
'balance' => preg_replace(
'/[^0-9,.]+/',
'',
$this->getFloat($data, 'client.balance')
),
'paid_to_date' => preg_replace(
'/[^0-9,.]+/',
'',
$this->getFloat($data, 'client.paid_to_date')
),
'credit_balance' => 0,
'settings' => $settings,
'client_hash' => Str::random(40),
'contacts' => [
[
'first_name' => $this->getString( $data, 'contact.first_name' ),
'last_name' => $this->getString( $data, 'contact.last_name' ),
'email' => $this->getString( $data, 'contact.email' ),
'phone' => $this->getString( $data, 'contact.phone' ),
'custom_value1' => $this->getString( $data, 'contact.custom_value1' ),
'custom_value2' => $this->getString( $data, 'contact.custom_value2' ),
'custom_value3' => $this->getString( $data, 'contact.custom_value3' ),
'custom_value4' => $this->getString( $data, 'contact.custom_value4' ),
'first_name' => $this->getString(
$data,
'contact.first_name'
),
'last_name' => $this->getString($data, 'contact.last_name'),
'email' => $this->getString($data, 'contact.email'),
'phone' => $this->getString($data, 'contact.phone'),
'custom_value1' => $this->getString(
$data,
'contact.custom_value1'
),
'custom_value2' => $this->getString(
$data,
'contact.custom_value2'
),
'custom_value3' => $this->getString(
$data,
'contact.custom_value3'
),
'custom_value4' => $this->getString(
$data,
'contact.custom_value4'
),
],
],
'country_id' => isset( $data['client.country'] ) ? $this->getCountryId( $data['client.country']) : null,
'shipping_country_id' => isset($data['client.shipping_country'] ) ? $this->getCountryId( $data['client.shipping_country'] ) : null,
'country_id' => isset($data['client.country'])
? $this->getCountryId($data['client.country'])
: null,
'shipping_country_id' => isset($data['client.shipping_country'])
? $this->getCountryId($data['client.shipping_country'])
: null,
];
}
}
}

View File

@ -88,6 +88,8 @@ class ProcessPostmarkWebhook implements ShouldQueue
return $this->processBounce();
case 'SpamComplaint':
return $this->processSpamComplaint();
case 'Open':
return $this->processOpen();
default:
# code...
break;
@ -95,6 +97,59 @@ class ProcessPostmarkWebhook implements ShouldQueue
}
// {
// "Metadata": {
// "example": "value",
// "example_2": "value"
// },
// "RecordType": "Open",
// "FirstOpen": true,
// "Client": {
// "Name": "Chrome 35.0.1916.153",
// "Company": "Google",
// "Family": "Chrome"
// },
// "OS": {
// "Name": "OS X 10.7 Lion",
// "Company": "Apple Computer, Inc.",
// "Family": "OS X 10"
// },
// "Platform": "WebMail",
// "UserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36",
// "ReadSeconds": 5,
// "Geo": {
// "CountryISOCode": "RS",
// "Country": "Serbia",
// "RegionISOCode": "VO",
// "Region": "Autonomna Pokrajina Vojvodina",
// "City": "Novi Sad",
// "Zip": "21000",
// "Coords": "45.2517,19.8369",
// "IP": "188.2.95.4"
// },
// "MessageID": "00000000-0000-0000-0000-000000000000",
// "MessageStream": "outbound",
// "ReceivedAt": "2022-02-06T06:37:48Z",
// "Tag": "welcome-email",
// "Recipient": "john@example.com"
// }
private function processOpen()
{
$this->invitation->opened_date = now();
$this->invitation->save();
SystemLogger::dispatch($this->request,
SystemLog::CATEGORY_MAIL,
SystemLog::EVENT_MAIL_OPENED,
SystemLog::TYPE_WEBHOOK_RESPONSE,
$this->invitation->contact->client,
$this->invitation->company
);
}
// {
// "RecordType": "Delivery",
// "ServerID": 23,

View File

@ -19,7 +19,6 @@ use Illuminate\Contracts\Queue\ShouldQueue;
class CreateAccountActivity implements ShouldQueue
{
/**
* Create the event listener.
*
@ -39,11 +38,10 @@ class CreateAccountActivity implements ShouldQueue
{
MultiDB::setDb($event->company->db);
if(Ninja::isHosted())
{
$nmo = new NinjaMailerObject;
if (Ninja::isHosted()) {
$nmo = new NinjaMailerObject();
$nmo->mailable = new \Modules\Admin\Mail\Welcome($event->user);
$nmo->company = $event->company;
$nmo->company = $event->company;
$nmo->settings = $event->company->settings;
$nmo->to_user = $event->user;

View File

@ -41,9 +41,11 @@ class ArchivedClientActivity implements ShouldQueue
{
MultiDB::setDb($event->company->db);
$fields = new stdClass;
$fields = new stdClass();
$user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->client->user_id;
$user_id = array_key_exists('user_id', $event->event_vars)
? $event->event_vars['user_id']
: $event->client->user_id;
$fields->client_id = $event->client->id;
$fields->user_id = $user_id;

View File

@ -43,9 +43,11 @@ class ClientUpdatedActivity implements ShouldQueue
$client = $event->client;
$fields = new stdClass;
$fields = new stdClass();
$user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->client->user_id;
$user_id = array_key_exists('user_id', $event->event_vars)
? $event->event_vars['user_id']
: $event->client->user_id;
$fields->client_id = $client->id;
$fields->user_id = $user_id;

View File

@ -43,7 +43,7 @@ class BouncedEmail extends Mailable
return
$this->from(config('mail.from.address'), config('mail.from.name'))
->text()
->text('bounced mail')
->subject($subject);
}

View File

@ -97,6 +97,7 @@ class Company extends BaseModel
'use_comma_as_decimal_place',
'report_include_drafts',
'client_registration_fields',
'convert_rate_to_client',
];
protected $hidden = [

View File

@ -90,6 +90,7 @@ class Invoice extends BaseModel
'subscription_id',
'auto_bill_enabled',
'uses_inclusive_taxes',
'vendor_id',
];
protected $casts = [

View File

@ -50,6 +50,7 @@ class SystemLog extends Model
const EVENT_MAIL_BOUNCED = 32;
const EVENT_MAIL_SPAM_COMPLAINT = 33;
const EVENT_MAIL_DELIVERY = 34;
const EVENT_MAIL_OPENED = 35;
const EVENT_WEBHOOK_RESPONSE = 40;
const EVENT_WEBHOOK_SUCCESS = 41;

View File

@ -58,11 +58,16 @@ class CreditCard
public function paymentView(array $data)
{
$description = $this->stripe->decodeUnicodeString(ctrans('texts.invoices') . ': ' . collect($data['invoices'])->pluck('invoice_number')) . " for client {$this->stripe->client->present()->name()}";
$payment_intent_data = [
'amount' => $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()),
'currency' => $this->stripe->client->getCurrencyCode(),
'customer' => $this->stripe->findOrCreateCustomer(),
'description' => $this->stripe->decodeUnicodeString(ctrans('texts.invoices') . ': ' . collect($data['invoices'])->pluck('invoice_number')),
'description' => $description,
'metadata' => [
'payment_hash' => $this->stripe->payment_hash->hash,
'gateway_type_id' => GatewayType::CREDIT_CARD,

View File

@ -211,6 +211,9 @@ class PaymentRepository extends BaseRepository {
$payment->currency_id = $client_currency;
}
$payment->currency_id = $company_currency;
return $payment;
}

View File

@ -104,7 +104,7 @@ class ApplyPayment extends AbstractService
->ledger()
->updatePaymentBalance($amount_paid);
nlog("updating client balance by amount {$amount_paid}");
// nlog("updating client balance by amount {$amount_paid}");
$this->invoice
->client

View File

@ -33,8 +33,8 @@ class MarkSent extends AbstractService
public function run()
{
/* Return immediately if status is not draft */
if ($this->invoice && $this->invoice->fresh()->status_id != Invoice::STATUS_DRAFT) {
/* Return immediately if status is not draft or invoice has been deleted */
if ($this->invoice && ($this->invoice->fresh()->status_id != Invoice::STATUS_DRAFT || $this->invoice->is_deleted)) {
return $this->invoice;
}

View File

@ -382,22 +382,23 @@ document.addEventListener('DOMContentLoaded', function() {
return $converter->convertToHtml($markdown);
}
public function processMarkdownOnLineItems(array &$items): void
{
foreach ($items as $key => $item) {
foreach ($item as $variable => $value) {
$item[$variable] = DesignHelpers::parseMarkdownToHtml($value ?? '');
}
// public function processMarkdownOnLineItems(array &$items): void
// {
// foreach ($items as $key => $item) {
// foreach ($item as $variable => $value) {
// $item[$variable] = DesignHelpers::parseMarkdownToHtml($value ?? '');
// }
$items[$key] = $item;
}
}
// $items[$key] = $item;
// }
// }
public function processNewLines(array &$items): void
{
foreach ($items as $key => $item) {
foreach ($item as $variable => $value) {
$item[$variable] = nl2br($value);
// $item[$variable] = nl2br($value, true);
$item[$variable] = str_replace( "\n", '<br>', $value);
}
$items[$key] = $item;

View File

@ -51,6 +51,7 @@ class PdfMaker
$this->commonmark = new CommonMarkConverter([
'allow_unsafe_links' => false,
// 'html_input' => 'allow',
]);
}

View File

@ -92,7 +92,9 @@ trait PdfMakerUtilities
$contains_html = false;
if ($child['element'] !== 'script') {
if (array_key_exists('process_markdown', $this->data) && $this->data['process_markdown']) {
if (array_key_exists('process_markdown', $this->data) && array_key_exists('content', $child) && $this->data['process_markdown']) {
$child['content'] = str_replace("<br>", "\r", $child['content']);
$child['content'] = $this->commonmark->convertToHtml($child['content'] ?? '');
}
}

View File

@ -166,6 +166,7 @@ class CompanyTransformer extends EntityTransformer
'use_comma_as_decimal_place' => (bool) $company->use_comma_as_decimal_place,
'report_include_drafts' => (bool) $company->report_include_drafts,
'client_registration_fields' => (array) $company->client_registration_fields,
'convert_rate_to_client' => (bool) $company->convert_rate_to_client,
];
}

View File

@ -44,7 +44,6 @@
"eway/eway-rapid-php": "^1.3",
"fakerphp/faker": "^1.14",
"fideloper/proxy": "^4.2",
"firebase/php-jwt": "^5",
"fruitcake/laravel-cors": "^2.0",
"gocardless/gocardless-pro": "^4.12",
"google/apiclient": "^2.7",

View File

@ -14,8 +14,8 @@ return [
'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
'app_version' => '5.3.55',
'app_tag' => '5.3.55',
'app_version' => '5.3.56',
'app_tag' => '5.3.56',
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', ''),

View File

@ -11,7 +11,7 @@ return [
* Threshold level for the N+1 query detection. If a relation query will be
* executed more then this amount, the detector will notify you about it.
*/
'threshold' => (int) env('QUERY_DETECTOR_THRESHOLD', 1),
'threshold' => (int) env('QUERY_DETECTOR_THRESHOLD', 3),
/*
* Here you can whitelist model relations.

View File

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddClientCurrencyConversionToCompaniesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('companies', function (Blueprint $table) {
$table->boolean('convert_rate_to_client')->default(true);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
}

View File

@ -3,42 +3,42 @@ const MANIFEST = 'flutter-app-manifest';
const TEMP = 'flutter-temp-cache';
const CACHE_NAME = 'flutter-app-cache';
const RESOURCES = {
"/": "4ea1489c95094645045520fdaafef6f2",
"version.json": "a00481850d5c63ba5df4e22636643438",
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
"main.dart.js": "5e3f699799092154b0778aa2553de0e6",
"canvaskit/profiling/canvaskit.wasm": "6d1b0fc1ec88c3110db88caa3393c580",
"canvaskit/profiling/canvaskit.js": "3783918f48ef691e230156c251169480",
"canvaskit/canvaskit.wasm": "b179ba02b7a9f61ebc108f82c5a1ecdb",
"canvaskit/canvaskit.js": "62b9906717d7215a6ff4cc24efbd1b5c",
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
"favicon.ico": "51636d3a390451561744c42188ccd628",
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5",
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "015400679694f1f51047e46da0e1dc98",
"assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1",
"assets/NOTICES": "224588855e3a5da7f1c34f0db1837839",
"assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3",
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
"assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978",
"assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
"assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc",
"assets/assets/images/icon.png": "090f69e23311a4b6d851b3880ae52541",
"assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a",
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
"assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5",
"assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
"assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868",
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
"assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5",
"assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc",
"assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
"assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
"assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71",
"assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978",
"assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024",
"assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c",
"assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71",
"assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219",
"assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a",
"assets/assets/images/icon.png": "090f69e23311a4b6d851b3880ae52541"
"assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3",
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "015400679694f1f51047e46da0e1dc98",
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5",
"assets/NOTICES": "224588855e3a5da7f1c34f0db1837839",
"assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1",
"favicon.ico": "51636d3a390451561744c42188ccd628",
"/": "085942208d4abcfcbd9e14bccdbc445c",
"version.json": "a00481850d5c63ba5df4e22636643438",
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
"main.dart.js": "1b02a5309c32514f339260abc3d5cb02",
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
"canvaskit/profiling/canvaskit.js": "3783918f48ef691e230156c251169480",
"canvaskit/profiling/canvaskit.wasm": "6d1b0fc1ec88c3110db88caa3393c580",
"canvaskit/canvaskit.js": "62b9906717d7215a6ff4cc24efbd1b5c",
"canvaskit/canvaskit.wasm": "b179ba02b7a9f61ebc108f82c5a1ecdb"
};
// The application shell files that are downloaded before a service worker can

155981
public/main.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

203835
public/main.foss.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

153313
public/main.html.dart.js vendored

File diff suppressed because one or more lines are too long

203375
public/main.next.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -8,6 +8,7 @@
*
* @license https://opensource.org/licenses/AAL
*/
namespace Tests\Feature\Import\CSV;
use App\Import\Providers\Csv;
@ -39,98 +40,313 @@ class CsvImportTest extends TestCase
use MakesHash;
use MockAccountData;
public function setUp() :void
public function setUp(): void
{
parent::setUp();
$this->withoutMiddleware(
ThrottleRequests::class
);
$this->withoutMiddleware(ThrottleRequests::class);
config(['database.default' => config('ninja.db.default')]);
$this->makeTestData();
$this->withoutExceptionHandling();
}
public function testCsvFeature()
public function testExpenseCsvImport()
{
$csv = file_get_contents(base_path().'/tests/Feature/Import/clients.csv');
$csv = file_get_contents(
base_path() . '/tests/Feature/Import/expenses.csv'
);
$hash = Str::random(32);
$column_map = [
1 => 'client.balance',
2 => 'client.paid_to_date',
0 => 'client.name',
19 => 'client.currency_id',
20 => 'client.public_notes',
21 => 'client.private_notes',
22 => 'contact.first_name',
23 => 'contact.last_name',
24 => 'contact.email',
0 => 'expense.client',
1 => 'expense.project',
2 => 'expense.public_notes',
3 => 'expense.amount',
];
$data = [
'hash' => $hash,
'column_map' => [ 'client' => [ 'mapping' => $column_map ] ],
'hash' => $hash,
'column_map' => ['expense' => ['mapping' => $column_map]],
'skip_header' => true,
'import_type' => 'csv',
];
Cache::put( $hash . '-client', base64_encode( $csv ), 360 );
Cache::put($hash . '-expense', base64_encode($csv), 360);
$csv_importer = new Csv($data, $this->company);
$count = $csv_importer->import('expense');
$base_transformer = new BaseTransformer($this->company);
$this->assertTrue($base_transformer->hasProject('officiis'));
}
public function testVendorCsvImport()
{
$csv = file_get_contents(
base_path() . '/tests/Feature/Import/vendors.csv'
);
$hash = Str::random(32);
$column_map = [
0 => 'vendor.name',
19 => 'vendor.currency_id',
20 => 'vendor.public_notes',
21 => 'vendor.private_notes',
22 => 'vendor.first_name',
23 => 'vendor.last_name',
];
$data = [
'hash' => $hash,
'column_map' => ['vendor' => ['mapping' => $column_map]],
'skip_header' => true,
'import_type' => 'csv',
];
$pre_import = Vendor::count();
Cache::put($hash . '-vendor', base64_encode($csv), 360);
$csv_importer = new Csv($data, $this->company);
$csv_importer->import('vendor');
$base_transformer = new BaseTransformer($this->company);
$this->assertTrue($base_transformer->hasVendor('Ludwig Krajcik DVM'));
}
public function testProductImport()
{
$csv = file_get_contents(
base_path() . '/tests/Feature/Import/products.csv'
);
$hash = Str::random(32);
Cache::put($hash . '-product', base64_encode($csv), 360);
$column_map = [
1 => 'product.product_key',
2 => 'product.notes',
3 => 'product.cost',
];
$data = [
'hash' => $hash,
'column_map' => ['product' => ['mapping' => $column_map]],
'skip_header' => true,
'import_type' => 'csv',
];
$csv_importer = new Csv($data, $this->company);
$this->assertInstanceOf(Csv::class, $csv_importer);
$csv_importer->import('product');
$base_transformer = new BaseTransformer($this->company);
$this->assertTrue($base_transformer->hasProduct('officiis'));
// $this->assertTrue($base_transformer->hasProduct('maxime'));
}
public function testClientImport()
{
$csv = file_get_contents(
base_path() . '/tests/Feature/Import/clients.csv'
);
$hash = Str::random(32);
$column_map = [
1 => 'client.balance',
2 => 'client.paid_to_date',
0 => 'client.name',
19 => 'client.currency_id',
20 => 'client.public_notes',
21 => 'client.private_notes',
22 => 'contact.first_name',
23 => 'contact.last_name',
24 => 'contact.email',
];
$data = [
'hash' => $hash,
'column_map' => ['client' => ['mapping' => $column_map]],
'skip_header' => true,
'import_type' => 'csv',
];
Cache::put($hash . '-client', base64_encode($csv), 360);
$csv_importer = new Csv($data, $this->company);
$this->assertInstanceOf(Csv::class, $csv_importer);
$csv_importer->import('client');
$base_transformer = new BaseTransformer($this->company);
$this->assertTrue($base_transformer->hasClient("Ludwig Krajcik DVM"));
$this->assertTrue($base_transformer->hasClient('Ludwig Krajcik DVM'));
$client_id = $base_transformer->getClient("Ludwig Krajcik DVM", null);
$client_id = $base_transformer->getClient('Ludwig Krajcik DVM', null);
$c = Client::find($client_id);
$this->assertEquals($client_id, $c->id);
$client_id = $base_transformer->getClient("a non existent clent", "brook59@example.org");
$client_id = $base_transformer->getClient(
'a non existent clent',
'brook59@example.org'
);
$this->assertEquals($client_id, $c->id);
}
public function testInvoiceImport()
{
/*Need to import clients first*/
$csv = file_get_contents(
base_path() . '/tests/Feature/Import/clients.csv'
);
$hash = Str::random(32);
$column_map = [
1 => 'client.balance',
2 => 'client.paid_to_date',
0 => 'client.name',
19 => 'client.currency_id',
20 => 'client.public_notes',
21 => 'client.private_notes',
22 => 'contact.first_name',
23 => 'contact.last_name',
];
$data = [
'hash' => $hash,
'column_map' => ['client' => ['mapping' => $column_map]],
'skip_header' => true,
'import_type' => 'csv',
];
Cache::put($hash . '-client', base64_encode($csv), 360);
$csv_importer = new Csv($data, $this->company);
$this->assertInstanceOf(Csv::class, $csv_importer);
$csv_importer->import('client');
$base_transformer = new BaseTransformer($this->company);
$this->assertTrue($base_transformer->hasClient('Ludwig Krajcik DVM'));
/* client import verified*/
/*Now import invoices*/
$csv = file_get_contents(
base_path() . '/tests/Feature/Import/invoice.csv'
);
$hash = Str::random(32);
$column_map = [
1 => 'client.email',
3 => 'payment.amount',
5 => 'invoice.po_number',
8 => 'invoice.due_date',
9 => 'item.discount',
11 => 'invoice.partial_due_date',
12 => 'invoice.public_notes',
13 => 'invoice.private_notes',
0 => 'client.name',
2 => 'invoice.number',
7 => 'invoice.date',
14 => 'item.product_key',
15 => 'item.notes',
16 => 'item.cost',
17 => 'item.quantity',
];
$data = [
'hash' => $hash,
'column_map' => ['invoice' => ['mapping' => $column_map]],
'skip_header' => true,
'import_type' => 'csv',
];
Cache::put($hash . '-invoice', base64_encode($csv), 360);
$csv_importer = new Csv($data, $this->company);
$csv_importer->import('invoice');
$this->assertTrue($base_transformer->hasInvoice('801'));
/* Lets piggy back payments tests here to save rebuilding the test multiple times*/
$csv = file_get_contents(
base_path() . '/tests/Feature/Import/payments.csv'
);
$hash = Str::random(32);
$column_map = [
0 => 'payment.client_id',
1 => 'payment.invoice_number',
2 => 'payment.amount',
3 => 'payment.date',
];
$data = [
'hash' => $hash,
'column_map' => ['payment' => ['mapping' => $column_map]],
'skip_header' => true,
'import_type' => 'csv',
];
Cache::put($hash . '-payment', base64_encode($csv), 360);
$csv_importer = new Csv($data, $this->company);
$csv_importer->import('payment');
$this->assertTrue($base_transformer->hasInvoice('801'));
$invoice_id = $base_transformer->getInvoiceId('801');
$invoice = Invoice::find($invoice_id);
$this->assertTrue($invoice->payments()->exists());
$this->assertEquals(1, $invoice->payments()->count());
$this->assertEquals(400, $invoice->payments()->sum('payments.amount'));
}
}
// public function testClientCsvImport()
// {
// $csv = file_get_contents(base_path().'/tests/Feature/Import/clients.csv');
// $hash = Str::random(32);
// $column_map = [
// 1 => 'client.balance',
// 2 => 'client.paid_to_date',
// 0 => 'client.name',
// 19 => 'client.currency_id',
// 20 => 'client.public_notes',
// 21 => 'client.private_notes',
// 22 => 'contact.first_name',
// 23 => 'contact.last_name',
// ];
// public function testClientCsvImport()
// {
// $csv = file_get_contents(base_path().'/tests/Feature/Import/clients.csv');
// $hash = Str::random(32);
// $column_map = [
// 1 => 'client.balance',
// 2 => 'client.paid_to_date',
// 0 => 'client.name',
// 19 => 'client.currency_id',
// 20 => 'client.public_notes',
// 21 => 'client.private_notes',
// 22 => 'contact.first_name',
// 23 => 'contact.last_name',
// ];
// $data = [
// 'hash' => $hash,
// 'column_map' => [ 'client' => [ 'mapping' => $column_map ] ],
// 'skip_header' => true,
// 'import_type' => 'csv',
// ];
// $data = [
// 'hash' => $hash,
// 'column_map' => [ 'client' => [ 'mapping' => $column_map ] ],
// 'skip_header' => true,
// 'import_type' => 'csv',
// ];
// $pre_import = Client::count();
// $pre_import = Client::count();
// Cache::put( $hash . '-client', base64_encode( $csv ), 360 );
// Cache::put( $hash . '-client', base64_encode( $csv ), 360 );
// CSVImport::dispatchNow( $data, $this->company );
// CSVImport::dispatchNow( $data, $this->company );
// $this->assertGreaterThan( $pre_import, Client::count() );
// }
// $this->assertGreaterThan( $pre_import, Client::count() );
// }

View File

@ -10,9 +10,11 @@
*/
namespace Tests\Feature;
use App\Helpers\Invoice\InvoiceSum;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Invoice;
use App\Repositories\InvoiceRepository;
use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\DatabaseTransactions;
@ -43,6 +45,62 @@ class InvoiceTest extends TestCase
$this->makeTestData();
}
public function testMarkingDeletedInvoiceAsSent()
{
Client::factory()->create(['user_id' => $this->user->id, 'company_id' => $this->company->id])->each(function ($c) {
ClientContact::factory()->create([
'user_id' => $this->user->id,
'client_id' => $c->id,
'company_id' => $this->company->id,
'is_primary' => 1,
]);
ClientContact::factory()->create([
'user_id' => $this->user->id,
'client_id' => $c->id,
'company_id' => $this->company->id,
]);
});
$client = Client::all()->first();
$invoice = Invoice::factory()->create(['user_id' => $this->user->id, 'company_id' => $this->company->id, 'client_id' => $this->client->id]);
$invoice->status_id = Invoice::STATUS_DRAFT;
$invoice->line_items = $this->buildLineItems();
$invoice->uses_inclusive_taxes = false;
$invoice->tax_rate1 = 0;
$invoice->tax_rate2 = 0;
$invoice->tax_rate3 = 0;
$invoice->discount = 0;
$invoice->save();
$invoice_calc = new InvoiceSum($invoice);
$invoice_calc->build();
$invoice = $invoice_calc->getInvoice();
$invoice->save();
$this->assertEquals(Invoice::STATUS_DRAFT, $invoice->status_id);
$this->assertEquals(10, $invoice->amount);
$this->assertEquals(0, $invoice->balance);
$invoice_repository = new InvoiceRepository();
$invoice = $invoice_repository->delete($invoice);
$this->assertEquals(10, $invoice->amount);
$this->assertEquals(0, $invoice->balance);
$this->assertTrue($invoice->is_deleted);
$invoice->service()->markSent()->save();
$this->assertEquals(0, $invoice->balance);
}
public function testInvoiceList()
{
Client::factory()->create(['user_id' => $this->user->id, 'company_id' => $this->company->id])->each(function ($c) {

View File

@ -208,7 +208,9 @@ class InvoiceItemTest extends TestCase
$item->cost = 10;
$item->is_amount_discount = true;
$item->discount = 2.521254522145214511;
$item->tax_name1 = "GST";
$item->tax_rate1 = 10;
$item->tax_name2 = "VAT";
$item->tax_rate2 = 17.5;
$this->invoice->line_items = [$item];

View File

@ -251,7 +251,9 @@ class InvoiceItemV2Test extends TestCase
$item->cost = 10;
$item->is_amount_discount = true;
$item->discount = 2.521254522145214511;
$item->tax_name1 = 'GST';
$item->tax_rate1 = 10;
$item->tax_name2 = 'VAT';
$item->tax_rate2 = 17.5;
$this->invoice->line_items = [$item];