From ad68d64dca7f36a11434dec90edf24d080c4efe1 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 21 Apr 2016 22:01:17 +0300 Subject: [PATCH 001/120] Changes for white label license --- app/Console/Commands/SendRenewalInvoices.php | 8 +++++-- app/Http/Middleware/StartupCheck.php | 2 ++ app/Http/routes.php | 1 + .../2016_04_16_103943_enterprise_plan.php | 23 ++++++++++++++----- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/app/Console/Commands/SendRenewalInvoices.php b/app/Console/Commands/SendRenewalInvoices.php index 64db2fad4fb1..87a840aed2c0 100644 --- a/app/Console/Commands/SendRenewalInvoices.php +++ b/app/Console/Commands/SendRenewalInvoices.php @@ -62,8 +62,12 @@ class SendRenewalInvoices extends Command $invoice->due_date = date('Y-m-d', strtotime('+ 10 days')); $invoice->save(); - $this->mailer->sendInvoice($invoice); - $this->info("Sent invoice to {$client->getDisplayName()}"); + if ($term == PLAN_TERM_YEARLY) { + $this->mailer->sendInvoice($invoice); + $this->info("Sent {$term}ly {$plan} invoice to {$client->getDisplayName()}"); + } else { + $this->info("Created {$term}ly {$plan} invoice for {$client->getDisplayName()}"); + } } $this->info('Done'); diff --git a/app/Http/Middleware/StartupCheck.php b/app/Http/Middleware/StartupCheck.php index be96ade97efe..63197550d9c0 100644 --- a/app/Http/Middleware/StartupCheck.php +++ b/app/Http/Middleware/StartupCheck.php @@ -142,7 +142,9 @@ class StartupCheck } elseif ($productId == PRODUCT_WHITE_LABEL) { if ($data == 'valid') { $company = Auth::user()->account->company; + $company->plan_term = PLAN_TERM_YEARLY; $company->plan_paid = date_create()->format('Y-m-d'); + $company->plan_expires = date_create()->modify('+1 year')->format('Y-m-d'); $company->plan = PLAN_WHITE_LABEL; $company->save(); diff --git a/app/Http/routes.php b/app/Http/routes.php index 48f06e5e7b3d..e5915b394a2b 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -554,6 +554,7 @@ if (!defined('CONTACT_EMAIL')) { define('NINJA_GATEWAY_CONFIG', 'NINJA_GATEWAY_CONFIG'); define('NINJA_WEB_URL', 'https://www.invoiceninja.com'); define('NINJA_APP_URL', 'https://app.invoiceninja.com'); + define('NINJA_DATE', '2000-01-01'); define('NINJA_VERSION', '2.5.1.3'); define('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja'); diff --git a/database/migrations/2016_04_16_103943_enterprise_plan.php b/database/migrations/2016_04_16_103943_enterprise_plan.php index 8ca5632ad358..8a3a63717367 100644 --- a/database/migrations/2016_04_16_103943_enterprise_plan.php +++ b/database/migrations/2016_04_16_103943_enterprise_plan.php @@ -127,13 +127,24 @@ class EnterprisePlan extends Migration $company->plan_started = $primaryAccount->pro_plan_paid; $company->plan_paid = $primaryAccount->pro_plan_paid; + $expires = DateTime::createFromFormat('Y-m-d', $primaryAccount->pro_plan_paid); + $expires->modify('+1 year'); + $expires = $expires->format('Y-m-d'); + + // check for self host white label licenses if (!Utils::isNinjaProd()) { - $company->plan = 'white_label'; - $company->plan_term = null; - } elseif ($company->plan_paid != '2000-01-01'/* NINJA_DATE*/) { - $expires = DateTime::createFromFormat('Y-m-d', $primaryAccount->pro_plan_paid); - $expires->modify('+1 year'); - $company->plan_expires = $expires->format('Y-m-d'); + if ($company->plan_paid) { + $company->plan = 'white_label'; + // old ones were unlimited, new ones are yearly + if ($company->plan_paid == NINJA_DATE) { + $company->plan_term = null; + } else { + $company->plan_term = PLAN_TERM_YEARLY; + $company->plan_expires = $expires; + } + } + } elseif ($company->plan_paid != NINJA_DATE) { + $company->plan_expires = $expires; } } From 07f50d188f801181880af3be72be77ba208d3216 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 24 Apr 2016 00:08:16 +0300 Subject: [PATCH 002/120] Fix for partial invoice label on PDF --- public/built.js | 25 +++++++++---------------- public/js/pdf.pdfmake.js | 25 +++++++++---------------- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/public/built.js b/public/built.js index 0522b23e67c3..faffe4a2c348 100644 --- a/public/built.js +++ b/public/built.js @@ -31601,25 +31601,18 @@ NINJA.invoiceDetails = function(invoice) { ]) } - var isPartial = NINJA.parseFloat(invoice.partial); - - if (NINJA.parseFloat(invoice.balance) < NINJA.parseFloat(invoice.amount)) { - data.push([ - {text: invoiceLabels.balance_due}, - {text: formatMoneyInvoice(invoice.amount, invoice)} - ]); - } else if (isPartial) { - data.push([ - {text: invoiceLabels.balance_due}, - {text: formatMoneyInvoice(invoice.total_amount, invoice)} - ]); - } - data.push([ - {text: isPartial ? invoiceLabels.partial_due : invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']}, - {text: formatMoneyInvoice(invoice.balance_amount, invoice), style: ['invoiceDetailBalanceDue']} + {text: invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']}, + {text: formatMoneyInvoice(invoice.total_amount, invoice), style: ['invoiceDetailBalanceDue']} ]) + if (NINJA.parseFloat(invoice.partial)) { + data.push([ + {text: invoiceLabels.partial_due, style: ['invoiceDetailBalanceDueLabel']}, + {text: formatMoneyInvoice(invoice.balance_amount, invoice), style: ['invoiceDetailBalanceDue']} + ]) + } + return NINJA.prepareDataPairs(data, 'invoiceDetails'); } diff --git a/public/js/pdf.pdfmake.js b/public/js/pdf.pdfmake.js index 1e5d1e93b3f8..db8e16f79442 100644 --- a/public/js/pdf.pdfmake.js +++ b/public/js/pdf.pdfmake.js @@ -597,25 +597,18 @@ NINJA.invoiceDetails = function(invoice) { ]) } - var isPartial = NINJA.parseFloat(invoice.partial); - - if (NINJA.parseFloat(invoice.balance) < NINJA.parseFloat(invoice.amount)) { - data.push([ - {text: invoiceLabels.balance_due}, - {text: formatMoneyInvoice(invoice.amount, invoice)} - ]); - } else if (isPartial) { - data.push([ - {text: invoiceLabels.balance_due}, - {text: formatMoneyInvoice(invoice.total_amount, invoice)} - ]); - } - data.push([ - {text: isPartial ? invoiceLabels.partial_due : invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']}, - {text: formatMoneyInvoice(invoice.balance_amount, invoice), style: ['invoiceDetailBalanceDue']} + {text: invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']}, + {text: formatMoneyInvoice(invoice.total_amount, invoice), style: ['invoiceDetailBalanceDue']} ]) + if (NINJA.parseFloat(invoice.partial)) { + data.push([ + {text: invoiceLabels.partial_due, style: ['invoiceDetailBalanceDueLabel']}, + {text: formatMoneyInvoice(invoice.balance_amount, invoice), style: ['invoiceDetailBalanceDue']} + ]) + } + return NINJA.prepareDataPairs(data, 'invoiceDetails'); } From 604c132a3a0405657a4d3eeee252bdb7c9f67ebf Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 24 Apr 2016 16:14:35 +0300 Subject: [PATCH 003/120] Temporary workaround to support not setting the CVC/CVV --- app/Services/PaymentService.php | 8 ++++++-- resources/views/payments/payment.blade.php | 11 ++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/app/Services/PaymentService.php b/app/Services/PaymentService.php index 61af6cd2336c..858a175f8954 100644 --- a/app/Services/PaymentService.php +++ b/app/Services/PaymentService.php @@ -98,9 +98,13 @@ class PaymentService extends BaseService 'number' => isset($input['card_number']) ? $input['card_number'] : null, 'expiryMonth' => isset($input['expiration_month']) ? $input['expiration_month'] : null, 'expiryYear' => isset($input['expiration_year']) ? $input['expiration_year'] : null, - 'cvv' => isset($input['cvv']) ? $input['cvv'] : '', ]; - + + // allow space until there's a setting to disable + if (isset($input['cvv']) && $input['cvv'] != ' ') { + $data['cvv'] = $input['cvv']; + } + if (isset($input['country_id'])) { $country = Country::find($input['country_id']); diff --git a/resources/views/payments/payment.blade.php b/resources/views/payments/payment.blade.php index 48ff913c4ea3..274361938bac 100644 --- a/resources/views/payments/payment.blade.php +++ b/resources/views/payments/payment.blade.php @@ -20,10 +20,14 @@ address_zip: $('#postal_code').val(), address_country: $("#country_id option:selected").text(), number: $('#card_number').val(), - cvc: $('#cvv').val(), exp_month: $('#expiration_month').val(), exp_year: $('#expiration_year').val() }; + + // allow space until there's a setting to disable + if ($('#cvv').val() != ' ') { + data.cvc = $('#cvv').val(); + } // Validate the card details if (!Stripe.card.validateCardNumber(data.number)) { @@ -34,11 +38,12 @@ $('#js-error-message').html('{{ trans('texts.invalid_expiry') }}').fadeIn(); return false; } - if (!Stripe.card.validateCVC(data.cvc)) { + + if (data.hasOwnProperty('cvc') && !Stripe.card.validateCVC(data.cvc)) { $('#js-error-message').html('{{ trans('texts.invalid_cvv') }}').fadeIn(); return false; } - + // Disable the submit button to prevent repeated clicks $form.find('button').prop('disabled', true); $('#js-error-message').hide(); From 71b1a9c209d6219e4d0bdaf6995f68c33c248356 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 24 Apr 2016 16:34:48 +0300 Subject: [PATCH 004/120] Improved filtering the payment list --- app/Ninja/Repositories/PaymentRepository.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/Ninja/Repositories/PaymentRepository.php b/app/Ninja/Repositories/PaymentRepository.php index a027cb62aedb..85dfab9964f8 100644 --- a/app/Ninja/Repositories/PaymentRepository.php +++ b/app/Ninja/Repositories/PaymentRepository.php @@ -64,7 +64,14 @@ class PaymentRepository extends BaseRepository if ($filter) { $query->where(function ($query) use ($filter) { - $query->where('clients.name', 'like', '%'.$filter.'%'); + $query->where('clients.name', 'like', '%'.$filter.'%') + ->orWhere('invoices.invoice_number', 'like', '%'.$filter.'%') + ->orWhere('payments.transaction_reference', 'like', '%'.$filter.'%') + ->orWhere('gateways.name', 'like', '%'.$filter.'%') + ->orWhere('payment_types.name', 'like', '%'.$filter.'%') + ->orWhere('contacts.first_name', 'like', '%'.$filter.'%') + ->orWhere('contacts.last_name', 'like', '%'.$filter.'%') + ->orWhere('contacts.email', 'like', '%'.$filter.'%'); }); } From cfb4dc43515777c0a4bea9004951bae354c3eeb9 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 24 Apr 2016 20:41:30 +0300 Subject: [PATCH 005/120] Hide zapier button for resellers --- resources/views/accounts/api_tokens.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/accounts/api_tokens.blade.php b/resources/views/accounts/api_tokens.blade.php index 9c328e6b2643..49029bf8d2b3 100644 --- a/resources/views/accounts/api_tokens.blade.php +++ b/resources/views/accounts/api_tokens.blade.php @@ -6,7 +6,7 @@
{!! Button::normal(trans('texts.documentation'))->asLinkTo(NINJA_WEB_URL.'/api-documentation/')->withAttributes(['target' => '_blank'])->appendIcon(Icon::create('info-sign')) !!} - @if (Utils::isNinja()) + @if (Utils::isNinja() && !Utils::isReseller()) {!! Button::normal(trans('texts.zapier'))->asLinkTo(ZAPIER_URL)->withAttributes(['target' => '_blank']) !!} @endif @if (Utils::hasFeature(FEATURE_API)) From f28e0cd76b0cd3cd7210da558105b12ee235010d Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 26 Apr 2016 12:41:34 +0300 Subject: [PATCH 006/120] Fix for JS rounding problem --- resources/views/invoices/knockout.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/invoices/knockout.blade.php b/resources/views/invoices/knockout.blade.php index 8fe4bcbcce80..cd4ad5f73d2b 100644 --- a/resources/views/invoices/knockout.blade.php +++ b/resources/views/invoices/knockout.blade.php @@ -460,7 +460,7 @@ function InvoiceModel(data) { }); self.totals.rawPaidToDate = ko.computed(function() { - return accounting.toFixed(self.amount(),2) - accounting.toFixed(self.balance(),2); + return roundToTwo(accounting.toFixed(self.amount(),2) - accounting.toFixed(self.balance(),2)); }); self.totals.paidToDate = ko.computed(function() { From 996b6d4ce6b375fed464b890f58b5a0e506e4238 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 26 Apr 2016 21:11:41 +0300 Subject: [PATCH 007/120] Fixed typo in text files --- resources/lang/da/texts.php | 2 +- resources/lang/de/texts.php | 2 +- resources/lang/en/texts.php | 2 +- resources/lang/es/texts.php | 2 +- resources/lang/es_ES/texts.php | 2 +- resources/lang/fr/texts.php | 2 +- resources/lang/fr_CA/texts.php | 2 +- resources/lang/it/texts.php | 2 +- resources/lang/ja/texts.php | 2 +- resources/lang/lt/texts.php | 2 +- resources/lang/nb_NO/texts.php | 2 +- resources/lang/nl/texts.php | 2 +- resources/lang/pt_BR/texts.php | 2 +- resources/lang/sv/texts.php | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/resources/lang/da/texts.php b/resources/lang/da/texts.php index ffc74871cde0..2b9e21c2415a 100644 --- a/resources/lang/da/texts.php +++ b/resources/lang/da/texts.php @@ -493,7 +493,7 @@ return array( 'invoice_history' => 'Faktura historik', 'quote_history' => 'Tilbuds historik', 'current_version' => 'Nuværende version', - 'select_versiony' => 'Vælg version', + 'select_version' => 'Vælg version', 'view_history' => 'Vis historik', 'edit_payment' => 'Redigér betaling', diff --git a/resources/lang/de/texts.php b/resources/lang/de/texts.php index 08ffaf7b8a0b..c41dae9d08e2 100644 --- a/resources/lang/de/texts.php +++ b/resources/lang/de/texts.php @@ -493,7 +493,7 @@ return array( 'invoice_history' => 'Rechnungshistorie', 'quote_history' => 'Angebotshistorie', 'current_version' => 'Aktuelle Version', - 'select_versiony' => 'Version auswählen', + 'select_version' => 'Version auswählen', 'view_history' => 'Historie anzeigen', 'edit_payment' => 'Zahlung bearbeiten', diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 37478c5d73e3..f101c79564e7 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -423,7 +423,7 @@ $LANG = array( 'invoice_history' => 'Invoice History', 'quote_history' => 'Quote History', 'current_version' => 'Current version', - 'select_versiony' => 'Select version', + 'select_version' => 'Select version', 'view_history' => 'View History', 'edit_payment' => 'Edit Payment', 'updated_payment' => 'Successfully updated payment', diff --git a/resources/lang/es/texts.php b/resources/lang/es/texts.php index 75bd1a75dae6..f5f68bc527f8 100644 --- a/resources/lang/es/texts.php +++ b/resources/lang/es/texts.php @@ -466,7 +466,7 @@ return array( 'invoice_history' => 'Facturar Historial', 'quote_history' => 'Cotizar Historial', 'current_version' => 'Versión actual', - 'select_versiony' => 'Seleccionar versión', + 'select_version' => 'Seleccionar versión', 'view_history' => 'Ver Historial', 'edit_payment' => 'Editar Pago', diff --git a/resources/lang/es_ES/texts.php b/resources/lang/es_ES/texts.php index 25054072dac5..55c38f712352 100644 --- a/resources/lang/es_ES/texts.php +++ b/resources/lang/es_ES/texts.php @@ -486,7 +486,7 @@ return array( 'invoice_history' => 'Historial de Facturas', 'quote_history' => 'Historial de Presupuestos', 'current_version' => 'Versión Actual', - 'select_versiony' => 'Seleccione la Versión', + 'select_version' => 'Seleccione la Versión', 'view_history' => 'Ver Historial', 'edit_payment' => 'Editar Pago', diff --git a/resources/lang/fr/texts.php b/resources/lang/fr/texts.php index 6cd75a9f0efa..215235a32413 100644 --- a/resources/lang/fr/texts.php +++ b/resources/lang/fr/texts.php @@ -486,7 +486,7 @@ return array( 'invoice_history' => 'Historique des factures', 'quote_history' => 'Historique des devis', 'current_version' => 'Version courante', - 'select_versiony' => 'Choix de la verison', + 'select_version' => 'Choix de la verison', 'view_history' => 'Consulter l\'historique', 'edit_payment' => 'Editer le paiement', diff --git a/resources/lang/fr_CA/texts.php b/resources/lang/fr_CA/texts.php index c62227e45050..e6e4c79f0fe2 100644 --- a/resources/lang/fr_CA/texts.php +++ b/resources/lang/fr_CA/texts.php @@ -487,7 +487,7 @@ return array( 'invoice_history' => 'Historique des factures', 'quote_history' => 'Historique des soumissions', 'current_version' => 'Version courante', - 'select_versiony' => 'Choix de la verison', + 'select_version' => 'Choix de la verison', 'view_history' => 'Consulter l\'historique', 'edit_payment' => 'Éditer le paiement', diff --git a/resources/lang/it/texts.php b/resources/lang/it/texts.php index 154b9f3e5d21..17d77d5d4881 100644 --- a/resources/lang/it/texts.php +++ b/resources/lang/it/texts.php @@ -489,7 +489,7 @@ return array( 'invoice_history' => 'Invoice History', 'quote_history' => 'Quote History', 'current_version' => 'Current version', - 'select_versiony' => 'Select version', + 'select_version' => 'Select version', 'view_history' => 'View History', 'edit_payment' => 'Edit Payment', diff --git a/resources/lang/ja/texts.php b/resources/lang/ja/texts.php index 7e673ac11d33..1bfa46e4749a 100644 --- a/resources/lang/ja/texts.php +++ b/resources/lang/ja/texts.php @@ -424,7 +424,7 @@ $LANG = array( 'invoice_history' => '請求履歴', 'quote_history' => '見積履歴', 'current_version' => '現在のバージョン', - 'select_versiony' => 'バージョンを選択', + 'select_version' => 'バージョンを選択', 'view_history' => '履歴を閲覧', 'edit_payment' => '支払いを編集', 'updated_payment' => '支払いを更新しました', diff --git a/resources/lang/lt/texts.php b/resources/lang/lt/texts.php index 252a7f03bd9c..dc6c18154c6c 100644 --- a/resources/lang/lt/texts.php +++ b/resources/lang/lt/texts.php @@ -497,7 +497,7 @@ return array( 'invoice_history' => 'Invoice History', 'quote_history' => 'Quote History', 'current_version' => 'Current version', - 'select_versiony' => 'Select version', + 'select_version' => 'Select version', 'view_history' => 'View History', 'edit_payment' => 'Edit Payment', diff --git a/resources/lang/nb_NO/texts.php b/resources/lang/nb_NO/texts.php index df06d7a32547..d9b2158ac3a9 100644 --- a/resources/lang/nb_NO/texts.php +++ b/resources/lang/nb_NO/texts.php @@ -493,7 +493,7 @@ return array( 'invoice_history' => 'Faktura Historikk', 'quote_history' => 'Tilbuds Historikk', 'current_version' => 'Nåværende versjon', - 'select_versiony' => 'Velg versjon', + 'select_version' => 'Velg versjon', 'view_history' => 'Vis Historikk', 'edit_payment' => 'Rediger Betaling', diff --git a/resources/lang/nl/texts.php b/resources/lang/nl/texts.php index 26c639245bb9..d3b0b045dfe3 100644 --- a/resources/lang/nl/texts.php +++ b/resources/lang/nl/texts.php @@ -489,7 +489,7 @@ return array( 'invoice_history' => 'Factuurgeschiedenis', 'quote_history' => 'Offertegeschiedenis', 'current_version' => 'Huidige versie', - 'select_versiony' => 'Selecteer versie', + 'select_version' => 'Selecteer versie', 'view_history' => 'Bekijk geschiedenis', 'edit_payment' => 'Bewerk betaling', diff --git a/resources/lang/pt_BR/texts.php b/resources/lang/pt_BR/texts.php index 3b180e93d6ad..4db4e84b9fee 100644 --- a/resources/lang/pt_BR/texts.php +++ b/resources/lang/pt_BR/texts.php @@ -487,7 +487,7 @@ return array( 'invoice_history' => 'Histórico de Faturas', 'quote_history' => 'Histórico de Orçamentos', 'current_version' => 'Versão Atual', - 'select_versiony' => 'Selecionar versão', + 'select_version' => 'Selecionar versão', 'view_history' => 'Visualizar Histórico', 'edit_payment' => 'Editar Pagamento', diff --git a/resources/lang/sv/texts.php b/resources/lang/sv/texts.php index 050076bd6e3e..745313fd14fd 100644 --- a/resources/lang/sv/texts.php +++ b/resources/lang/sv/texts.php @@ -492,7 +492,7 @@ return array( 'invoice_history' => 'Fakturahistorik', 'quote_history' => 'Offerthistorik', 'current_version' => 'Nuvarande version', - 'select_versiony' => 'Välj version', + 'select_version' => 'Välj version', 'view_history' => 'Visa historik', 'edit_payment' => 'Ändra betalning', From 6ba27c24757985a36ac14ae615320d027307cc7f Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 27 Apr 2016 11:49:54 +0300 Subject: [PATCH 008/120] Fixed problem sorting data tables by client --- app/Ninja/Repositories/CreditRepository.php | 2 +- app/Ninja/Repositories/ExpenseRepository.php | 2 +- app/Ninja/Repositories/InvoiceRepository.php | 6 +++--- app/Ninja/Repositories/PaymentRepository.php | 4 ++-- app/Ninja/Repositories/TaskRepository.php | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/Ninja/Repositories/CreditRepository.php b/app/Ninja/Repositories/CreditRepository.php index 9803b4628af8..4381ae25771c 100644 --- a/app/Ninja/Repositories/CreditRepository.php +++ b/app/Ninja/Repositories/CreditRepository.php @@ -27,7 +27,7 @@ class CreditRepository extends BaseRepository DB::raw('COALESCE(clients.currency_id, accounts.currency_id) currency_id'), DB::raw('COALESCE(clients.country_id, accounts.country_id) country_id'), 'credits.public_id', - 'clients.name as client_name', + DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'credits.amount', diff --git a/app/Ninja/Repositories/ExpenseRepository.php b/app/Ninja/Repositories/ExpenseRepository.php index 3492740d6a94..c3106d669878 100644 --- a/app/Ninja/Repositories/ExpenseRepository.php +++ b/app/Ninja/Repositories/ExpenseRepository.php @@ -96,7 +96,7 @@ class ExpenseRepository extends BaseRepository 'vendors.name as vendor_name', 'vendors.public_id as vendor_public_id', 'vendors.user_id as vendor_user_id', - 'clients.name as client_name', + DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'contacts.first_name', diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index b84536e88c9d..d5970af8125e 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -58,7 +58,7 @@ class InvoiceRepository extends BaseRepository 'clients.user_id as client_user_id', 'invoice_number', 'invoice_status_id', - 'clients.name as client_name', + DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'invoices.public_id', 'invoices.amount', 'invoices.balance', @@ -115,7 +115,7 @@ class InvoiceRepository extends BaseRepository DB::raw('COALESCE(clients.currency_id, accounts.currency_id) currency_id'), DB::raw('COALESCE(clients.country_id, accounts.country_id) country_id'), 'clients.public_id as client_public_id', - 'clients.name as client_name', + DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'invoices.public_id', 'invoices.amount', 'frequencies.name as frequency', @@ -169,7 +169,7 @@ class InvoiceRepository extends BaseRepository 'invoices.balance as balance', 'invoices.due_date', 'clients.public_id as client_public_id', - 'clients.name as client_name', + DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'invoices.public_id', 'invoices.amount', 'invoices.start_date', diff --git a/app/Ninja/Repositories/PaymentRepository.php b/app/Ninja/Repositories/PaymentRepository.php index 85dfab9964f8..9b7d9787b457 100644 --- a/app/Ninja/Repositories/PaymentRepository.php +++ b/app/Ninja/Repositories/PaymentRepository.php @@ -34,7 +34,7 @@ class PaymentRepository extends BaseRepository DB::raw('COALESCE(clients.currency_id, accounts.currency_id) currency_id'), DB::raw('COALESCE(clients.country_id, accounts.country_id) country_id'), 'payments.transaction_reference', - 'clients.name as client_name', + DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'payments.amount', @@ -101,7 +101,7 @@ class PaymentRepository extends BaseRepository 'invitations.invitation_key', 'payments.public_id', 'payments.transaction_reference', - 'clients.name as client_name', + DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'clients.public_id as client_public_id', 'payments.amount', 'payments.payment_date', diff --git a/app/Ninja/Repositories/TaskRepository.php b/app/Ninja/Repositories/TaskRepository.php index a7655a8abd9e..d78b53c02d56 100644 --- a/app/Ninja/Repositories/TaskRepository.php +++ b/app/Ninja/Repositories/TaskRepository.php @@ -25,7 +25,7 @@ class TaskRepository ->where('clients.deleted_at', '=', null) ->select( 'tasks.public_id', - 'clients.name as client_name', + \DB::raw("COALESCE(NULLIF(clients.name,''), NULLIF(CONCAT(contacts.first_name, ' ', contacts.last_name),''), NULLIF(contacts.email,'')) client_name"), 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'contacts.first_name', From 6f5a68e1f525729a380a9913dbe6070a8a75074b Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 27 Apr 2016 16:36:44 +0300 Subject: [PATCH 009/120] Fixed bug in vendor controller --- app/Http/Controllers/QuoteApiController.php | 75 --------------------- app/Http/Controllers/VendorController.php | 2 +- 2 files changed, 1 insertion(+), 76 deletions(-) delete mode 100644 app/Http/Controllers/QuoteApiController.php diff --git a/app/Http/Controllers/QuoteApiController.php b/app/Http/Controllers/QuoteApiController.php deleted file mode 100644 index 8111872b10d9..000000000000 --- a/app/Http/Controllers/QuoteApiController.php +++ /dev/null @@ -1,75 +0,0 @@ -invoiceRepo = $invoiceRepo; - } - - /** - * @SWG\Get( - * path="/quotes", - * tags={"quote"}, - * summary="List of quotes", - * @SWG\Response( - * response=200, - * description="A list with quotes", - * @SWG\Schema(type="array", @SWG\Items(ref="#/definitions/Invoice")) - * ), - * @SWG\Response( - * response="default", - * description="an ""unexpected"" error" - * ) - * ) - */ - public function index() - { - $paginator = Invoice::scope(); - $invoices = Invoice::scope() - ->with('client', 'invitations', 'user', 'invoice_items') - ->where('invoices.is_quote', '=', true); - - if ($clientPublicId = Input::get('client_id')) { - $filter = function($query) use ($clientPublicId) { - $query->where('public_id', '=', $clientPublicId); - }; - $invoices->whereHas('client', $filter); - $paginator->whereHas('client', $filter); - } - - $invoices = $invoices->orderBy('created_at', 'desc')->paginate(); - - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $paginator = $paginator->paginate(); - - $data = $this->createCollection($invoices, $transformer, 'quotes', $paginator); - - return $this->response($data); - } - - /* - public function store() - { - $data = Input::all(); - $invoice = $this->invoiceRepo->save(false, $data, false); - - $response = json_encode($invoice, JSON_PRETTY_PRINT); - $headers = Utils::getApiHeaders(); - return Response::make($response, 200, $headers); - } - */ -} diff --git a/app/Http/Controllers/VendorController.php b/app/Http/Controllers/VendorController.php index 00f5bbe83e4d..d53d6041fb18 100644 --- a/app/Http/Controllers/VendorController.php +++ b/app/Http/Controllers/VendorController.php @@ -155,7 +155,7 @@ class VendorController extends BaseController { $vendor = Vendor::scope($publicId)->with('vendorcontacts')->firstOrFail(); - $this->authorize('edit', $vendor) + $this->authorize('edit', $vendor); $data = [ 'vendor' => $vendor, From f77657f1c1c31959882b904afe4b48df1121adbf Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 27 Apr 2016 17:10:27 +0300 Subject: [PATCH 010/120] Update log filenames in travis file --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9e8b983e795d..083bbe4f2583 100644 --- a/.travis.yml +++ b/.travis.yml @@ -89,7 +89,8 @@ after_script: - mysql -u root -e 'select * from invoice_items;' ninja - mysql -u root -e 'select * from payments;' ninja - mysql -u root -e 'select * from credits;' ninja - - cat storage/logs/laravel.log + - cat storage/logs/laravel-error.log + - cat storage/logs/laravel-info.log notifications: email: From 4c4859917599fcd28ec2925c1cb5c7cb5cb4a9cb Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 27 Apr 2016 17:22:50 +0300 Subject: [PATCH 011/120] Fix for permissions in payment controller --- app/Http/Controllers/PaymentController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 4654a3e69856..223b43bc41c1 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -590,7 +590,7 @@ class PaymentController extends BaseController { $input = $request->input(); - $this->authorizeUpdate($data); + $this->authorizeUpdate($input); $input['invoice_id'] = Invoice::getPrivateId($input['invoice']); $input['client_id'] = Client::getPrivateId($input['client']); From b930593d5fdc260da232c8483a19090ad57cf4ad Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 27 Apr 2016 17:24:09 +0300 Subject: [PATCH 012/120] Fix for product policy --- app/Policies/ProductPolicy.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Policies/ProductPolicy.php b/app/Policies/ProductPolicy.php index d9dd62c34e9b..6bd9c56d0c6c 100644 --- a/app/Policies/ProductPolicy.php +++ b/app/Policies/ProductPolicy.php @@ -2,7 +2,7 @@ namespace App\Policies; -class VendorPolicy extends EntityPolicy { +class ProductPolicy extends EntityPolicy { public static function edit($user, $item) { return $user->hasPermission('admin'); } From df93ffaaf9963b9693d759e8a61e092248c0409c Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 27 Apr 2016 18:09:04 +0300 Subject: [PATCH 013/120] Fix for permissions in payment controller --- app/Http/Controllers/PaymentController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 223b43bc41c1..0a0fe1485676 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -610,7 +610,7 @@ class PaymentController extends BaseController { $input = $request->input(); - $this->authorizeUpdate($data); + $this->authorizeUpdate($input); $payment = $this->paymentRepo->save($input); From 4f926ec8b84a1f048a0a92d3e891deb577057946 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 27 Apr 2016 19:13:29 +0300 Subject: [PATCH 014/120] Fix for permissions in base service --- app/Services/BaseService.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Services/BaseService.php b/app/Services/BaseService.php index 90a7359910c2..6bbbc729b246 100644 --- a/app/Services/BaseService.php +++ b/app/Services/BaseService.php @@ -1,5 +1,6 @@ Date: Wed, 27 Apr 2016 19:25:24 +0300 Subject: [PATCH 015/120] Fix for recurring invoice list --- app/Ninja/Repositories/InvoiceRepository.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index d5970af8125e..a62d3aba8d25 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -125,7 +125,8 @@ class InvoiceRepository extends BaseRepository 'contacts.last_name', 'contacts.email', 'invoices.deleted_at', - 'invoices.is_deleted' + 'invoices.is_deleted', + 'invoices.user_id' ); if ($clientPublicId) { From ce2392563d34216e402f014a611b3e540b8a6404 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 27 Apr 2016 23:56:14 +0300 Subject: [PATCH 016/120] Removed checkSubPermissions flag --- app/Http/Controllers/ExpenseController.php | 2 +- app/Http/Controllers/InvoiceController.php | 4 ++-- app/Ninja/Repositories/ExpenseRepository.php | 4 ++-- app/Ninja/Repositories/InvoiceRepository.php | 13 ++++++------ app/Services/ExpenseService.php | 4 ++-- app/Services/InvoiceService.php | 21 +++++++++----------- 6 files changed, 22 insertions(+), 26 deletions(-) diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index e53122e46dcb..e65a485fa2f0 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -158,7 +158,7 @@ class ExpenseController extends BaseController $this->authorizeUpdate($data); - $expense = $this->expenseService->save($data, true); + $expense = $this->expenseService->save($data); Session::flash('message', trans('texts.updated_expense')); diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 79ce2aee93b1..7aa9ca2d4d14 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -405,7 +405,7 @@ class InvoiceController extends BaseController $action = Input::get('action'); $entityType = Input::get('entityType'); - $invoice = $this->invoiceService->save($data, true); + $invoice = $this->invoiceService->save($data); $entityType = $invoice->getEntityType(); $message = trans("texts.created_{$entityType}"); @@ -444,7 +444,7 @@ class InvoiceController extends BaseController $action = Input::get('action'); $entityType = Input::get('entityType'); - $invoice = $this->invoiceService->save($data, true); + $invoice = $this->invoiceService->save($data); $entityType = $invoice->getEntityType(); $message = trans("texts.updated_{$entityType}"); Session::flash('message', $message); diff --git a/app/Ninja/Repositories/ExpenseRepository.php b/app/Ninja/Repositories/ExpenseRepository.php index c3106d669878..62264f32d2b1 100644 --- a/app/Ninja/Repositories/ExpenseRepository.php +++ b/app/Ninja/Repositories/ExpenseRepository.php @@ -122,7 +122,7 @@ class ExpenseRepository extends BaseRepository return $query; } - public function save($input, $checkSubPermissions=false) + public function save($input) { $publicId = isset($input['public_id']) ? $input['public_id'] : false; @@ -160,7 +160,7 @@ class ExpenseRepository extends BaseRepository $document_ids = !empty($input['document_ids'])?array_map('intval', $input['document_ids']):array();; foreach ($document_ids as $document_id){ $document = Document::scope($document_id)->first(); - if($document && !$checkSubPermissions || Auth::user()->can('edit', $document)){ + if($document && Auth::user()->can('edit', $document)){ $document->invoice_id = null; $document->expense_id = $expense->id; $document->save(); diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index a62d3aba8d25..15616e9cd75f 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -198,7 +198,7 @@ class InvoiceRepository extends BaseRepository ->make(); } - public function save($data, $checkSubPermissions = false) + public function save($data) { $account = \Auth::user()->account; $publicId = isset($data['public_id']) ? $data['public_id'] : false; @@ -420,7 +420,7 @@ class InvoiceRepository extends BaseRepository $document_ids = !empty($data['document_ids'])?array_map('intval', $data['document_ids']):array();; foreach ($document_ids as $document_id){ $document = Document::scope($document_id)->first(); - if($document && !$checkSubPermissions || Auth::user()->can('edit', $document)){ + if($document && Auth::user()->can('edit', $document)){ if($document->invoice_id && $document->invoice_id != $invoice->id){ // From a clone @@ -473,7 +473,7 @@ class InvoiceRepository extends BaseRepository $task = false; if (isset($item['task_public_id']) && $item['task_public_id']) { $task = Task::scope($item['task_public_id'])->where('invoice_id', '=', null)->firstOrFail(); - if(!$checkSubPermissions || Auth::user()->can('edit', $task)){ + if(Auth::user()->can('edit', $task)){ $task->invoice_id = $invoice->id; $task->client_id = $invoice->client_id; $task->save(); @@ -483,7 +483,7 @@ class InvoiceRepository extends BaseRepository $expense = false; if (isset($item['expense_public_id']) && $item['expense_public_id']) { $expense = Expense::scope($item['expense_public_id'])->where('invoice_id', '=', null)->firstOrFail(); - if(!$checkSubPermissions || Auth::user()->can('edit', $expense)){ + if(Auth::user()->can('edit', $expense)){ $expense->invoice_id = $invoice->id; $expense->client_id = $invoice->client_id; $expense->save(); @@ -494,7 +494,7 @@ class InvoiceRepository extends BaseRepository if (\Auth::user()->account->update_products && ! strtotime($productKey)) { $product = Product::findProductByKey($productKey); if (!$product) { - if(!$checkSubPermissions || Auth::user()->can('create', ENTITY_PRODUCT)){ + if (Auth::user()->can('create', ENTITY_PRODUCT)) { $product = Product::createNew(); $product->product_key = trim($item['product_key']); } @@ -502,7 +502,7 @@ class InvoiceRepository extends BaseRepository $product = null; } } - if($product && (!$checkSubPermissions || Auth::user()->can('edit', $product))){ + if ($product && (Auth::user()->can('edit', $product))) { $product->notes = ($task || $expense) ? '' : $item['notes']; $product->cost = $expense ? 0 : $item['cost']; $product->save(); @@ -516,7 +516,6 @@ class InvoiceRepository extends BaseRepository $invoiceItem->notes = trim($invoice->is_recurring ? $item['notes'] : Utils::processVariables($item['notes'])); $invoiceItem->cost = Utils::parseFloat($item['cost']); $invoiceItem->qty = Utils::parseFloat($item['qty']); - //$invoiceItem->tax_rate = 0; if (isset($item['custom_value1'])) { $invoiceItem->custom_value1 = $item['custom_value1']; diff --git a/app/Services/ExpenseService.php b/app/Services/ExpenseService.php index afec1e4f9950..0b28a7c4d6b1 100644 --- a/app/Services/ExpenseService.php +++ b/app/Services/ExpenseService.php @@ -28,7 +28,7 @@ class ExpenseService extends BaseService return $this->expenseRepo; } - public function save($data, $checkSubPermissions=false) + public function save($data) { if (isset($data['client_id']) && $data['client_id']) { $data['client_id'] = Client::getPrivateId($data['client_id']); @@ -38,7 +38,7 @@ class ExpenseService extends BaseService $data['vendor_id'] = Vendor::getPrivateId($data['vendor_id']); } - return $this->expenseRepo->save($data, $checkSubPermissions); + return $this->expenseRepo->save($data); } public function getDatatable($search) diff --git a/app/Services/InvoiceService.php b/app/Services/InvoiceService.php index 66442ac0a31b..ecf0ad2fab0b 100644 --- a/app/Services/InvoiceService.php +++ b/app/Services/InvoiceService.php @@ -30,26 +30,23 @@ class InvoiceService extends BaseService return $this->invoiceRepo; } - public function save($data, $checkSubPermissions = false) + public function save($data) { if (isset($data['client'])) { - $canSaveClient = !$checkSubPermissions; - if( ! $canSaveClient){ - $clientPublicId = array_get($data, 'client.public_id') ?: array_get($data, 'client.id'); - if (empty($clientPublicId) || $clientPublicId == '-1') { - $canSaveClient = Auth::user()->can('create', ENTITY_CLIENT); - } else { - $canSaveClient = Auth::user()->can('edit', Client::scope($clientPublicId)->first()); - } - } - + $canSaveClient = false; + $clientPublicId = array_get($data, 'client.public_id') ?: array_get($data, 'client.id'); + if (empty($clientPublicId) || $clientPublicId == '-1') { + $canSaveClient = Auth::user()->can('create', ENTITY_CLIENT); + } else { + $canSaveClient = Auth::user()->can('edit', Client::scope($clientPublicId)->first()); + } if ($canSaveClient) { $client = $this->clientRepo->save($data['client']); $data['client_id'] = $client->id; } } - $invoice = $this->invoiceRepo->save($data, $checkSubPermissions); + $invoice = $this->invoiceRepo->save($data); $client = $invoice->client; $client->load('contacts'); From 882cfb79fd3936d3f7a2fcb394beeea258b681ce Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 28 Apr 2016 13:53:29 +0300 Subject: [PATCH 017/120] Refactored controller->entity to entityType --- app/Http/Controllers/BaseController.php | 8 ++++---- app/Http/Controllers/ClientController.php | 2 +- app/Http/Controllers/CreditController.php | 2 +- app/Http/Controllers/DocumentController.php | 2 +- app/Http/Controllers/ExpenseController.php | 2 +- app/Http/Controllers/InvoiceController.php | 2 +- app/Http/Controllers/PaymentController.php | 2 +- app/Http/Controllers/QuoteController.php | 2 +- app/Http/Controllers/TaskController.php | 2 +- app/Http/Controllers/VendorController.php | 4 ++-- 10 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index 66abc53e2459..867bd9e77a9f 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -10,7 +10,7 @@ class BaseController extends Controller { use DispatchesJobs, AuthorizesRequests; - protected $entity; + protected $entityType; /** * Setup the layout used by the controller. @@ -25,17 +25,17 @@ class BaseController extends Controller } protected function authorizeCreate() { - $this->authorize('create', $this->entity); + $this->authorize('create', $this->entityType); } protected function authorizeUpdate($input){ $creating = empty($input['public_id']) || $input['public_id'] == '-1'; if($creating){ - $this->authorize('create', $this->entity); + $this->authorize('create', $this->entityType); } else{ - $className = Utils::getEntityName($this->entity); + $className = Utils::getEntityName($this->entityType); $object = call_user_func(array("App\\Models\\{$className}", 'scope'), $input['public_id'])->firstOrFail(); $this->authorize('edit', $object); diff --git a/app/Http/Controllers/ClientController.php b/app/Http/Controllers/ClientController.php index 49988ab78b79..3ef8966d30b7 100644 --- a/app/Http/Controllers/ClientController.php +++ b/app/Http/Controllers/ClientController.php @@ -35,7 +35,7 @@ class ClientController extends BaseController { protected $clientService; protected $clientRepo; - protected $entity = ENTITY_CLIENT; + protected $entityType = ENTITY_CLIENT; public function __construct(ClientRepository $clientRepo, ClientService $clientService) { diff --git a/app/Http/Controllers/CreditController.php b/app/Http/Controllers/CreditController.php index 23577f951389..1e3586af1922 100644 --- a/app/Http/Controllers/CreditController.php +++ b/app/Http/Controllers/CreditController.php @@ -17,7 +17,7 @@ class CreditController extends BaseController { protected $creditRepo; protected $creditService; - protected $entity = ENTITY_CREDIT; + protected $entityType = ENTITY_CREDIT; public function __construct(CreditRepository $creditRepo, CreditService $creditService) { diff --git a/app/Http/Controllers/DocumentController.php b/app/Http/Controllers/DocumentController.php index c0e25e773d11..0720e1a5babf 100644 --- a/app/Http/Controllers/DocumentController.php +++ b/app/Http/Controllers/DocumentController.php @@ -15,7 +15,7 @@ use App\Ninja\Repositories\DocumentRepository; class DocumentController extends BaseController { protected $documentRepo; - protected $entity = ENTITY_DOCUMENT; + protected $entityType = ENTITY_DOCUMENT; public function __construct(DocumentRepository $documentRepo) { diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index e65a485fa2f0..88a493d3ea21 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -25,7 +25,7 @@ class ExpenseController extends BaseController // Expenses protected $expenseRepo; protected $expenseService; - protected $entity = ENTITY_EXPENSE; + protected $entityType = ENTITY_EXPENSE; public function __construct(ExpenseRepository $expenseRepo, ExpenseService $expenseService) { diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 7aa9ca2d4d14..c3627a01c301 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -37,7 +37,7 @@ class InvoiceController extends BaseController protected $documentRepo; protected $invoiceService; protected $recurringInvoiceService; - protected $entity = ENTITY_INVOICE; + protected $entityType = ENTITY_INVOICE; public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService, DocumentRepository $documentRepo, RecurringInvoiceService $recurringInvoiceService) { diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 0a0fe1485676..bd5fb85199cf 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -30,7 +30,7 @@ use App\Http\Requests\UpdatePaymentRequest; class PaymentController extends BaseController { - protected $entity = ENTITY_PAYMENT; + protected $entityType = ENTITY_PAYMENT; public function __construct(PaymentRepository $paymentRepo, InvoiceRepository $invoiceRepo, AccountRepository $accountRepo, ContactMailer $contactMailer, PaymentService $paymentService) { diff --git a/app/Http/Controllers/QuoteController.php b/app/Http/Controllers/QuoteController.php index e2228ab2a0c0..3a0062296098 100644 --- a/app/Http/Controllers/QuoteController.php +++ b/app/Http/Controllers/QuoteController.php @@ -33,7 +33,7 @@ class QuoteController extends BaseController protected $invoiceRepo; protected $clientRepo; protected $invoiceService; - protected $entity = ENTITY_INVOICE; + protected $entityType = ENTITY_INVOICE; public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService) { diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index 7ff19436ead2..a1118269ac51 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -22,7 +22,7 @@ class TaskController extends BaseController { protected $taskRepo; protected $taskService; - protected $entity = ENTITY_TASK; + protected $entityType = ENTITY_TASK; public function __construct(TaskRepository $taskRepo, InvoiceRepository $invoiceRepo, TaskService $taskService) { diff --git a/app/Http/Controllers/VendorController.php b/app/Http/Controllers/VendorController.php index d53d6041fb18..9340b926d4eb 100644 --- a/app/Http/Controllers/VendorController.php +++ b/app/Http/Controllers/VendorController.php @@ -25,12 +25,12 @@ use App\Services\VendorService; use App\Http\Requests\CreateVendorRequest; use App\Http\Requests\UpdateVendorRequest; -// vendor + class VendorController extends BaseController { protected $vendorService; protected $vendorRepo; - protected $entity = ENTITY_VENDOR; + protected $entityType = ENTITY_VENDOR; public function __construct(VendorRepository $vendorRepo, VendorService $vendorService) { From 38b6c94fe7cd7f49e6b49206ca4a7c3cb653a39f Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 28 Apr 2016 15:10:53 +0300 Subject: [PATCH 018/120] Fix for quote api controller in routes files --- app/Http/routes.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/routes.php b/app/Http/routes.php index e5915b394a2b..bd94056d8791 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -253,8 +253,8 @@ Route::group(['middleware' => 'api', 'prefix' => 'api/v1'], function() Route::get('accounts', 'AccountApiController@show'); Route::put('accounts', 'AccountApiController@update'); Route::resource('clients', 'ClientApiController'); - Route::get('quotes', 'QuoteApiController@index'); - Route::resource('quotes', 'QuoteApiController'); + //Route::get('quotes', 'QuoteApiController@index'); + //Route::resource('quotes', 'QuoteApiController'); Route::get('invoices', 'InvoiceApiController@index'); Route::resource('invoices', 'InvoiceApiController'); Route::get('payments', 'PaymentApiController@index'); From 9979eba5d9cd00628926e9e11a4e08a4c0e0e053 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 28 Apr 2016 15:16:33 +0300 Subject: [PATCH 019/120] Working on custom route model binding --- app/Http/Controllers/BaseController.php | 15 ++++++++--- app/Http/Controllers/ClientController.php | 20 +++++--------- app/Http/Controllers/CreditController.php | 2 +- app/Http/Controllers/DocumentController.php | 2 +- app/Http/Controllers/ExpenseController.php | 2 +- app/Http/Controllers/InvoiceController.php | 2 +- app/Http/Controllers/PaymentController.php | 2 +- app/Http/Controllers/QuoteController.php | 2 +- app/Http/Controllers/TaskController.php | 2 +- app/Http/Controllers/VendorController.php | 4 +-- app/Http/Requests/BaseRequest.php | 30 +++++++++++++++++++++ app/Http/Requests/ClientRequest.php | 21 +++++++++++++++ app/Http/Requests/CreateClientRequest.php | 9 +++---- app/Http/Requests/CreateCreditRequest.php | 9 +++---- app/Http/Requests/UpdateClientRequest.php | 9 +++---- app/Libraries/Utils.php | 5 ++++ app/Ninja/Repositories/ClientRepository.php | 6 +++-- app/Services/ClientService.php | 4 +-- 18 files changed, 98 insertions(+), 48 deletions(-) create mode 100644 app/Http/Requests/BaseRequest.php create mode 100644 app/Http/Requests/ClientRequest.php diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index 66abc53e2459..4a33946c7631 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -3,6 +3,7 @@ use App\Http\Middleware\PermissionsRequired; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; +use Input; use Auth; use Utils; @@ -10,7 +11,7 @@ class BaseController extends Controller { use DispatchesJobs, AuthorizesRequests; - protected $entity; + protected $entityType; /** * Setup the layout used by the controller. @@ -25,17 +26,23 @@ class BaseController extends Controller } protected function authorizeCreate() { - $this->authorize('create', $this->entity); + $this->authorize('create', $this->entityType); } + /* + protected function authorizeUpdate($entity) { + $this->authorize('edit', $entity); + } + */ + protected function authorizeUpdate($input){ $creating = empty($input['public_id']) || $input['public_id'] == '-1'; if($creating){ - $this->authorize('create', $this->entity); + $this->authorize('create', $this->entityType); } else{ - $className = Utils::getEntityName($this->entity); + $className = Utils::getEntityName($this->entityType); $object = call_user_func(array("App\\Models\\{$className}", 'scope'), $input['public_id'])->firstOrFail(); $this->authorize('edit', $object); diff --git a/app/Http/Controllers/ClientController.php b/app/Http/Controllers/ClientController.php index 49988ab78b79..c66c775947c8 100644 --- a/app/Http/Controllers/ClientController.php +++ b/app/Http/Controllers/ClientController.php @@ -28,6 +28,7 @@ use App\Models\Task; use App\Ninja\Repositories\ClientRepository; use App\Services\ClientService; +use App\Http\Requests\ClientRequest; use App\Http\Requests\CreateClientRequest; use App\Http\Requests\UpdateClientRequest; @@ -35,7 +36,7 @@ class ClientController extends BaseController { protected $clientService; protected $clientRepo; - protected $entity = ENTITY_CLIENT; + protected $entityType = ENTITY_CLIENT; public function __construct(ClientRepository $clientRepo, ClientService $clientService) { @@ -81,11 +82,7 @@ class ClientController extends BaseController */ public function store(CreateClientRequest $request) { - $data = $request->input(); - - $this->authorizeUpdate($data); - - $client = $this->clientService->save($data); + $client = $this->clientService->save($request->input()); Session::flash('message', trans('texts.created_client')); @@ -100,6 +97,7 @@ class ClientController extends BaseController */ public function show($publicId) { + //$client = $request->entity()->load('conacts'); $client = Client::withTrashed()->scope($publicId)->with('contacts', 'size', 'industry')->firstOrFail(); $this->authorize('view', $client); @@ -177,9 +175,9 @@ class ClientController extends BaseController * @param int $id * @return Response */ - public function edit($publicId) + public function edit(ClientRequest $request) { - $client = Client::scope($publicId)->with('contacts')->firstOrFail(); + $client = $request->entity(); $this->authorize('edit', $client); @@ -225,11 +223,7 @@ class ClientController extends BaseController */ public function update(UpdateClientRequest $request) { - $data = $request->input(); - - $this->authorizeUpdate($data); - - $client = $this->clientService->save($data); + $client = $this->clientService->save($request->input(), $request->entity()); Session::flash('message', trans('texts.updated_client')); diff --git a/app/Http/Controllers/CreditController.php b/app/Http/Controllers/CreditController.php index 23577f951389..1e3586af1922 100644 --- a/app/Http/Controllers/CreditController.php +++ b/app/Http/Controllers/CreditController.php @@ -17,7 +17,7 @@ class CreditController extends BaseController { protected $creditRepo; protected $creditService; - protected $entity = ENTITY_CREDIT; + protected $entityType = ENTITY_CREDIT; public function __construct(CreditRepository $creditRepo, CreditService $creditService) { diff --git a/app/Http/Controllers/DocumentController.php b/app/Http/Controllers/DocumentController.php index c0e25e773d11..0720e1a5babf 100644 --- a/app/Http/Controllers/DocumentController.php +++ b/app/Http/Controllers/DocumentController.php @@ -15,7 +15,7 @@ use App\Ninja\Repositories\DocumentRepository; class DocumentController extends BaseController { protected $documentRepo; - protected $entity = ENTITY_DOCUMENT; + protected $entityType = ENTITY_DOCUMENT; public function __construct(DocumentRepository $documentRepo) { diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index e65a485fa2f0..88a493d3ea21 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -25,7 +25,7 @@ class ExpenseController extends BaseController // Expenses protected $expenseRepo; protected $expenseService; - protected $entity = ENTITY_EXPENSE; + protected $entityType = ENTITY_EXPENSE; public function __construct(ExpenseRepository $expenseRepo, ExpenseService $expenseService) { diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 7aa9ca2d4d14..c3627a01c301 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -37,7 +37,7 @@ class InvoiceController extends BaseController protected $documentRepo; protected $invoiceService; protected $recurringInvoiceService; - protected $entity = ENTITY_INVOICE; + protected $entityType = ENTITY_INVOICE; public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService, DocumentRepository $documentRepo, RecurringInvoiceService $recurringInvoiceService) { diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 0a0fe1485676..bd5fb85199cf 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -30,7 +30,7 @@ use App\Http\Requests\UpdatePaymentRequest; class PaymentController extends BaseController { - protected $entity = ENTITY_PAYMENT; + protected $entityType = ENTITY_PAYMENT; public function __construct(PaymentRepository $paymentRepo, InvoiceRepository $invoiceRepo, AccountRepository $accountRepo, ContactMailer $contactMailer, PaymentService $paymentService) { diff --git a/app/Http/Controllers/QuoteController.php b/app/Http/Controllers/QuoteController.php index e2228ab2a0c0..3a0062296098 100644 --- a/app/Http/Controllers/QuoteController.php +++ b/app/Http/Controllers/QuoteController.php @@ -33,7 +33,7 @@ class QuoteController extends BaseController protected $invoiceRepo; protected $clientRepo; protected $invoiceService; - protected $entity = ENTITY_INVOICE; + protected $entityType = ENTITY_INVOICE; public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService) { diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index 7ff19436ead2..a1118269ac51 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -22,7 +22,7 @@ class TaskController extends BaseController { protected $taskRepo; protected $taskService; - protected $entity = ENTITY_TASK; + protected $entityType = ENTITY_TASK; public function __construct(TaskRepository $taskRepo, InvoiceRepository $invoiceRepo, TaskService $taskService) { diff --git a/app/Http/Controllers/VendorController.php b/app/Http/Controllers/VendorController.php index d53d6041fb18..9340b926d4eb 100644 --- a/app/Http/Controllers/VendorController.php +++ b/app/Http/Controllers/VendorController.php @@ -25,12 +25,12 @@ use App\Services\VendorService; use App\Http\Requests\CreateVendorRequest; use App\Http\Requests\UpdateVendorRequest; -// vendor + class VendorController extends BaseController { protected $vendorService; protected $vendorRepo; - protected $entity = ENTITY_VENDOR; + protected $entityType = ENTITY_VENDOR; public function __construct(VendorRepository $vendorRepo, VendorService $vendorService) { diff --git a/app/Http/Requests/BaseRequest.php b/app/Http/Requests/BaseRequest.php new file mode 100644 index 000000000000..f7be7a8de3cb --- /dev/null +++ b/app/Http/Requests/BaseRequest.php @@ -0,0 +1,30 @@ +entity) { + return $this->entity; + } + + //dd($this->clients); + $publicId = Input::get('public_id') ?: Input::get('id'); + + if ( ! $publicId) { + return null; + } + + $class = Utils::getEntityClass($this->entityType); + $this->entity = $class::scope($publicId)->withTrashed()->firstOrFail(); + + return $this->entity; + } +} diff --git a/app/Http/Requests/ClientRequest.php b/app/Http/Requests/ClientRequest.php new file mode 100644 index 000000000000..2b491c46c7e4 --- /dev/null +++ b/app/Http/Requests/ClientRequest.php @@ -0,0 +1,21 @@ +user()->can('create', ENTITY_CLIENT); } /** diff --git a/app/Http/Requests/CreateCreditRequest.php b/app/Http/Requests/CreateCreditRequest.php index f2dc44d31aa0..46d40972c8dc 100644 --- a/app/Http/Requests/CreateCreditRequest.php +++ b/app/Http/Requests/CreateCreditRequest.php @@ -1,9 +1,6 @@ -user()->can('create', ENTITY_CREDIT); } /** diff --git a/app/Http/Requests/UpdateClientRequest.php b/app/Http/Requests/UpdateClientRequest.php index b73e019c4964..c1cdebe1891e 100644 --- a/app/Http/Requests/UpdateClientRequest.php +++ b/app/Http/Requests/UpdateClientRequest.php @@ -1,9 +1,6 @@ -user()->can('edit', $this->entity()); } /** diff --git a/app/Libraries/Utils.php b/app/Libraries/Utils.php index e4ad99c6ce45..86107acfcd59 100644 --- a/app/Libraries/Utils.php +++ b/app/Libraries/Utils.php @@ -669,6 +669,11 @@ class Utils return $year + $offset; } + public static function getEntityClass($entityType) + { + return 'App\\Models\\' . static::getEntityName($entityType); + } + public static function getEntityName($entityType) { return ucwords(str_replace('_', ' ', $entityType)); diff --git a/app/Ninja/Repositories/ClientRepository.php b/app/Ninja/Repositories/ClientRepository.php index d3f943a07001..77ebcb7fd736 100644 --- a/app/Ninja/Repositories/ClientRepository.php +++ b/app/Ninja/Repositories/ClientRepository.php @@ -66,11 +66,13 @@ class ClientRepository extends BaseRepository return $query; } - public function save($data) + public function save($data, $client = null) { $publicId = isset($data['public_id']) ? $data['public_id'] : false; - if (!$publicId || $publicId == '-1') { + if ($client) { + // do nothing + } if (!$publicId || $publicId == '-1') { $client = Client::createNew(); } else { $client = Client::scope($publicId)->with('contacts')->firstOrFail(); diff --git a/app/Services/ClientService.php b/app/Services/ClientService.php index 6f7383b0fced..96357991c519 100644 --- a/app/Services/ClientService.php +++ b/app/Services/ClientService.php @@ -30,13 +30,13 @@ class ClientService extends BaseService return $this->clientRepo; } - public function save($data) + public function save($data, $client = null) { if (Auth::user()->account->isNinjaAccount() && isset($data['plan'])) { $this->ninjaRepo->updatePlanDetails($data['public_id'], $data); } - return $this->clientRepo->save($data); + return $this->clientRepo->save($data, $client); } public function getDatatable($search) From 1a47598aeaa8cb46cecdac011c221d2ae8306b44 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 28 Apr 2016 15:20:34 +0300 Subject: [PATCH 020/120] Working on custom route model binding --- app/Http/Controllers/ClientController.php | 2 +- app/Http/Requests/BaseRequest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/ClientController.php b/app/Http/Controllers/ClientController.php index c66c775947c8..37786b401b53 100644 --- a/app/Http/Controllers/ClientController.php +++ b/app/Http/Controllers/ClientController.php @@ -184,7 +184,7 @@ class ClientController extends BaseController $data = [ 'client' => $client, 'method' => 'PUT', - 'url' => 'clients/'.$publicId, + 'url' => 'clients/'.$client->public_id, 'title' => trans('texts.edit_client'), ]; diff --git a/app/Http/Requests/BaseRequest.php b/app/Http/Requests/BaseRequest.php index f7be7a8de3cb..eb97a949a8f3 100644 --- a/app/Http/Requests/BaseRequest.php +++ b/app/Http/Requests/BaseRequest.php @@ -15,8 +15,8 @@ class BaseRequest extends Request { return $this->entity; } - //dd($this->clients); - $publicId = Input::get('public_id') ?: Input::get('id'); + $paramName = $this->entityType . 's'; + $publicId = $this->$paramName ?: (Input::get('public_id') ?: Input::get('id')); if ( ! $publicId) { return null; From 4b764ea72e86b983c5b0ead0cce62bdd0e363390 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 28 Apr 2016 15:23:09 +0300 Subject: [PATCH 021/120] Working on custom route model binding --- app/Http/Requests/ClientRequest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/Http/Requests/ClientRequest.php b/app/Http/Requests/ClientRequest.php index 2b491c46c7e4..e6470c6bd621 100644 --- a/app/Http/Requests/ClientRequest.php +++ b/app/Http/Requests/ClientRequest.php @@ -4,6 +4,11 @@ class ClientRequest extends BaseRequest { protected $entityType = ENTITY_CLIENT; + public function entity() + { + return parent::entity()->load('contacts'); + } + public function authorize() { return true; From 3ab4c0964d6dd3b51d8fa52b3bb0679667a15434 Mon Sep 17 00:00:00 2001 From: Philipp Hug Date: Thu, 28 Apr 2016 16:08:24 +0200 Subject: [PATCH 022/120] whitelabeling: allow more settings from environment. --- app/Http/routes.php | 52 ++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/app/Http/routes.php b/app/Http/routes.php index bd94056d8791..702cd1fcb98f 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -552,25 +552,25 @@ if (!defined('CONTACT_EMAIL')) { define('NINJA_ACCOUNT_KEY', 'zg4ylmzDkdkPOT8yoKQw9LTWaoZJx79h'); define('NINJA_GATEWAY_ID', GATEWAY_STRIPE); define('NINJA_GATEWAY_CONFIG', 'NINJA_GATEWAY_CONFIG'); - define('NINJA_WEB_URL', 'https://www.invoiceninja.com'); - define('NINJA_APP_URL', 'https://app.invoiceninja.com'); + define('NINJA_WEB_URL', env('NINJA_WEB_URL', 'https://www.invoiceninja.com')); + define('NINJA_APP_URL', env('NINJA_APP_URL', 'https://app.invoiceninja.com')); define('NINJA_DATE', '2000-01-01'); - define('NINJA_VERSION', '2.5.1.3'); + define('NINJA_VERSION', '2.5.1.3' . env('NINJA_VERSION_SUFFIX')); - define('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja'); - define('SOCIAL_LINK_TWITTER', 'https://twitter.com/invoiceninja'); - define('SOCIAL_LINK_GITHUB', 'https://github.com/invoiceninja/invoiceninja/'); + define('SOCIAL_LINK_FACEBOOK', env('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja')); + define('SOCIAL_LINK_TWITTER', env('SOCIAL_LINK_TWITTER', 'https://twitter.com/invoiceninja')); + define('SOCIAL_LINK_GITHUB', env('SOCIAL_LINK_GITHUB', 'https://github.com/invoiceninja/invoiceninja/')); - define('NINJA_FROM_EMAIL', 'maildelivery@invoiceninja.com'); - define('RELEASES_URL', 'https://trello.com/b/63BbiVVe/invoice-ninja'); - define('ZAPIER_URL', 'https://zapier.com/zapbook/invoice-ninja'); - define('OUTDATE_BROWSER_URL', 'http://browsehappy.com/'); - define('PDFMAKE_DOCS', 'http://pdfmake.org/playground.html'); - define('PHANTOMJS_CLOUD', 'http://api.phantomjscloud.com/api/browser/v2/'); - define('PHP_DATE_FORMATS', 'http://php.net/manual/en/function.date.php'); - define('REFERRAL_PROGRAM_URL', 'https://www.invoiceninja.com/referral-program/'); - define('EMAIL_MARKUP_URL', 'https://developers.google.com/gmail/markup'); - define('OFX_HOME_URL', 'http://www.ofxhome.com/index.php/home/directory/all'); + define('NINJA_FROM_EMAIL', env('NINJA_FROM_EMAIL', 'maildelivery@invoiceninja.com')); + define('RELEASES_URL', env('RELEASES_URL', 'https://trello.com/b/63BbiVVe/invoice-ninja')); + define('ZAPIER_URL', env('ZAPIER_URL', 'https://zapier.com/zapbook/invoice-ninja')); + define('OUTDATE_BROWSER_URL', env('OUTDATE_BROWSER_URL', 'http://browsehappy.com/')); + define('PDFMAKE_DOCS', env('PDFMAKE_DOCS', 'http://pdfmake.org/playground.html')); + define('PHANTOMJS_CLOUD', env('PHANTOMJS_CLOUD', 'http://api.phantomjscloud.com/api/browser/v2/')); + define('PHP_DATE_FORMATS', env('PHP_DATE_FORMATS', 'http://php.net/manual/en/function.date.php')); + define('REFERRAL_PROGRAM_URL', env('REFERRAL_PROGRAM_URL', 'https://www.invoiceninja.com/referral-program/')); + define('EMAIL_MARKUP_URL', env('EMAIL_MARKUP_URL', 'https://developers.google.com/gmail/markup')); + define('OFX_HOME_URL', env('OFX_HOME_URL', 'http://www.ofxhome.com/index.php/home/directory/all')); define('BLANK_IMAGE', 'data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='); @@ -584,13 +584,13 @@ if (!defined('CONTACT_EMAIL')) { define('INVOICE_DESIGNS_AFFILIATE_KEY', 'T3RS74'); define('SELF_HOST_AFFILIATE_KEY', '8S69AD'); - define('PRO_PLAN_PRICE', 50); - define('PLAN_PRICE_PRO_MONTHLY', 5); - define('PLAN_PRICE_PRO_YEARLY', 50); - define('PLAN_PRICE_ENTERPRISE_MONTHLY', 10); - define('PLAN_PRICE_ENTERPRISE_YEARLY', 100); - define('WHITE_LABEL_PRICE', 20); - define('INVOICE_DESIGNS_PRICE', 10); + define('PRO_PLAN_PRICE', env('PRO_PLAN_PRICE', 50)); + define('PLAN_PRICE_PRO_MONTHLY', env('PLAN_PRICE_PRO_MONTHLY', 5)); + define('PLAN_PRICE_PRO_YEARLY', env('PLAN_PRICE_PRO_YEARLY', 50)); + define('PLAN_PRICE_ENTERPRISE_MONTHLY', env('PLAN_PRICE_ENTERPRISE_MONTHLY', 10)); + define('PLAN_PRICE_ENTERPRISE_YEARLY', env('PLAN_PRICE_ENTERPRISE_YEARLY', 100)); + define('WHITE_LABEL_PRICE', env('WHITE_LABEL_PRICE', 20)); + define('INVOICE_DESIGNS_PRICE', env('INVOICE_DESIGNS_PRICE', 10)); define('USER_TYPE_SELF_HOST', 'SELF_HOST'); define('USER_TYPE_CLOUD_HOST', 'CLOUD_HOST'); @@ -600,8 +600,8 @@ if (!defined('CONTACT_EMAIL')) { define('TEST_PASSWORD', 'password'); define('API_SECRET', 'API_SECRET'); - define('IOS_PRODUCTION_PUSH','ninjaIOS'); - define('IOS_DEV_PUSH','devNinjaIOS'); + define('IOS_PRODUCTION_PUSH', env('IOS_PRODUCTION_PUSH', 'ninjaIOS')); + define('IOS_DEV_PUSH', env('IOS_DEV_PUSH', 'devNinjaIOS')); define('TOKEN_BILLING_DISABLED', 1); define('TOKEN_BILLING_OPT_IN', 2); @@ -770,4 +770,4 @@ if (Utils::isNinjaDev()) //ini_set('memory_limit','1024M'); //Auth::loginUsingId(1); } -*/ \ No newline at end of file +*/ From c9ac804e5fb93432925454797a3067401e352404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Gonz=C3=A1lez?= Date: Fri, 29 Apr 2016 21:54:38 +0200 Subject: [PATCH 023/120] Some more German translations I left out some strings when I was not completely sure about. --- resources/lang/de/texts.php | 102 ++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/resources/lang/de/texts.php b/resources/lang/de/texts.php index c41dae9d08e2..9b8c0e2311e7 100644 --- a/resources/lang/de/texts.php +++ b/resources/lang/de/texts.php @@ -1118,8 +1118,8 @@ return array( 'first_page' => 'Erste Seite', 'all_pages' => 'Alle Seiten', 'last_page' => 'Letzte Seite', - 'all_pages_header' => 'Show header on', - 'all_pages_footer' => 'Show footer on', + 'all_pages_header' => 'Zeige Header auf', + 'all_pages_footer' => 'Zeige Footer auf', 'invoice_currency' => 'Rechnungs-Währung', 'enable_https' => 'Wir empfehlen dringend HTTPS zu verwenden, um Kreditkarten online zu akzeptieren.', 'quote_issued_to' => 'Quote issued to', @@ -1133,72 +1133,72 @@ return array( 'white_label_text' => 'Purchase a ONE YEAR white label license for $'.WHITE_LABEL_PRICE.' to remove the Invoice Ninja branding from the client portal and help support our project.', 'navigation' => 'Navigation', - 'list_invoices' => 'List Invoices', - 'list_clients' => 'List Clients', - 'list_quotes' => 'List Quotes', - 'list_tasks' => 'List Tasks', - 'list_expenses' => 'List Expenses', - 'list_recurring_invoices' => 'List Recurring Invoices', - 'list_payments' => 'List Payments', + 'list_invoices' => 'Rechnungen Auflisten', + 'list_clients' => 'Klienten Auflisten', + 'list_quotes' => 'Angebote Auflisten', + 'list_tasks' => 'Aufgaben Auflisten', + 'list_expenses' => 'Ausgaben Auflisten', + 'list_recurring_invoices' => 'Wiederkehrende Rechnungen Auflisten', + 'list_payments' => 'Zahlungen Aufllisten', 'list_credits' => 'List Credits', - 'tax_name' => 'Tax Name', - 'report_settings' => 'Report Settings', + 'tax_name' => 'Steuer-Name', + 'report_settings' => 'Bericht-Einstellungen', 'search_hotkey' => 'shortcut is /', - 'new_user' => 'New User', - 'new_product' => 'New Product', - 'new_tax_rate' => 'New Tax Rate', + 'new_user' => 'Neuer Benutzer', + 'new_product' => 'Neues Produkt', + 'new_tax_rate' => 'Neue Steuersatz', 'invoiced_amount' => 'Invoiced Amount', 'invoice_item_fields' => 'Invoice Item Fields', - 'custom_invoice_item_fields_help' => 'Add a field when creating an invoice item and display the label and value on the PDF.', - 'recurring_invoice_number' => 'Recurring Invoice Number', + 'custom_invoice_item_fields_help' => 'Feld bei Erstellung eines Rechnungs-Elements hinzufügen, und dessen Bezeichnung und Wert im PDF anzeigen.', + 'recurring_invoice_number' => 'Wiederkehrende Rechnungsnummer', 'recurring_invoice_number_prefix_help' => 'Speciy a prefix to be added to the invoice number for recurring invoices. The default value is \'R\'.', 'enable_client_portal' => 'Dashboard', - 'enable_client_portal_help' => 'Show/hide the dashboard page in the client portal.', + 'enable_client_portal_help' => 'Dashboard-Seite im Klienten-Portal anzeigen/verbergen.', // Client Passwords - 'enable_portal_password'=>'Password protect invoices', - 'enable_portal_password_help'=>'Allows you to set a password for each contact. If a password is set, the contact will be required to enter a password before viewing invoices.', - 'send_portal_password'=>'Generate password automatically', - 'send_portal_password_help'=>'If no password is set, one will be generated and sent with the first invoice.', + 'enable_portal_password'=>'Sichere Rechnungen mit einem Passwort ab', + 'enable_portal_password_help'=>'Erlaubt, ein Passwort für jeden Kontakt zu setzen. Wenn ein Passwort gesetzt ist, wird dieses vom Kontakt benötigt, um Rechnungen anzusehen.', + 'send_portal_password'=>'Passwort automatisch generieren', + 'send_portal_password_help'=>'Wenn kein Passwort gesetzt ist, wird eines erstellt und mit der ersten Rechnung mit versendet.', - 'expired' => 'Expired', - 'invalid_card_number' => 'The credit card number is not valid.', - 'invalid_expiry' => 'The expiration date is not valid.', - 'invalid_cvv' => 'The CVV is not valid.', - 'cost' => 'Cost', - 'create_invoice_for_sample' => 'Note: create your first invoice to see a preview here.', + 'expired' => 'Abgelaufen', + 'invalid_card_number' => 'Die Kreditkartennummer ist nicht gültig.', + 'invalid_expiry' => 'Das Ablaufdatum ist nicht gültig.', + 'invalid_cvv' => 'Das CVV ist nicht gültig.', + 'cost' => 'Kosten', + 'create_invoice_for_sample' => 'Info: Erstelle deine erste Rechnung, um hier eine Voransicht zu sehen.', // User Permissions - 'owner' => 'Owner', + 'owner' => 'Eigentümer', 'administrator' => 'Administrator', - 'administrator_help' => 'Allow user to manage users, change settings and modify all records', - 'user_create_all' => 'Create clients, invoices, etc.', - 'user_view_all' => 'View all clients, invoices, etc.', - 'user_edit_all' => 'Edit all clients, invoices, etc.', - 'gateway_help_20' => ':link to sign up for Sage Pay.', - 'gateway_help_21' => ':link to sign up for Sage Pay.', - 'partial_due' => 'Partial Due', - 'restore_vendor' => 'Restore Vendor', - 'restored_vendor' => 'Successfully restored vendor', - 'restored_expense' => 'Successfully restored expense', - 'permissions' => 'Permissions', - 'create_all_help' => 'Allow user to create and modify records', - 'view_all_help' => 'Allow user to view records they didn\'t create', - 'edit_all_help' => 'Allow user to modify records they didn\'t create', - 'view_payment' => 'View Payment', + 'administrator_help' => 'Erlaube Benutzern, andere Benutzer zu administrieren, Einstellungen und alle Einträge zu verändern', + 'user_create_all' => 'Klienten, Rechnungen, etc. erstellen', + 'user_view_all' => 'Klienten, Rechnungen, etc. ansehen', + 'user_edit_all' => 'Klienten, Rechnungen, etc. bearbeiten', + 'gateway_help_20' => ':link um sich bei Sage Pay zu registrieren.', + 'gateway_help_21' => ':link um sich bei Sage Pay zu registrieren.', + 'partial_due' => 'Offener Teilbetrag', + 'restore_vendor' => 'Anbieter Wiederherstellen', + 'restored_vendor' => 'Anbieter erfolgreich wiederhergestellt', + 'restored_expense' => 'Ausgabe erfolgreich wiederhergestellt', + 'permissions' => 'Berechtigungen', + 'create_all_help' => 'Erlaube Benutzer, Einträge zu erstellen und zu verändern', + 'view_all_help' => 'Erlaube Benutzer, Einträge zu sehen, die er nicht selbst erstellt hat', + 'edit_all_help' => 'Erlaube Benutzer, Einträge zu verändern, die er nicht selbst erstellt hat', + 'view_payment' => 'Zahlung Ansehen', - 'january' => 'January', - 'february' => 'February', - 'march' => 'March', + 'january' => 'Jänner', + 'february' => 'Februar', + 'march' => 'März', 'april' => 'April', - 'may' => 'May', - 'june' => 'June', - 'july' => 'July', + 'may' => 'Mai', + 'june' => 'Juni', + 'july' => 'Juli', 'august' => 'August', 'september' => 'September', - 'october' => 'October', + 'october' => 'Oktober', 'november' => 'November', - 'december' => 'December', + 'december' => 'Dezember', ); From ac57dcdcc31273d748b4b4bdb07b4c98cd6dc82d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Gonz=C3=A1lez?= Date: Fri, 29 Apr 2016 21:57:18 +0200 Subject: [PATCH 024/120] Changed "Klient" to "Kunde" to be consistent. --- resources/lang/de/texts.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/lang/de/texts.php b/resources/lang/de/texts.php index 9b8c0e2311e7..2520f229465a 100644 --- a/resources/lang/de/texts.php +++ b/resources/lang/de/texts.php @@ -1134,7 +1134,7 @@ return array( 'navigation' => 'Navigation', 'list_invoices' => 'Rechnungen Auflisten', - 'list_clients' => 'Klienten Auflisten', + 'list_clients' => 'Kunden Auflisten', 'list_quotes' => 'Angebote Auflisten', 'list_tasks' => 'Aufgaben Auflisten', 'list_expenses' => 'Ausgaben Auflisten', @@ -1154,7 +1154,7 @@ return array( 'recurring_invoice_number' => 'Wiederkehrende Rechnungsnummer', 'recurring_invoice_number_prefix_help' => 'Speciy a prefix to be added to the invoice number for recurring invoices. The default value is \'R\'.', 'enable_client_portal' => 'Dashboard', - 'enable_client_portal_help' => 'Dashboard-Seite im Klienten-Portal anzeigen/verbergen.', + 'enable_client_portal_help' => 'Dashboard-Seite im Kunden-Portal anzeigen/verbergen.', // Client Passwords 'enable_portal_password'=>'Sichere Rechnungen mit einem Passwort ab', @@ -1173,9 +1173,9 @@ return array( 'owner' => 'Eigentümer', 'administrator' => 'Administrator', 'administrator_help' => 'Erlaube Benutzern, andere Benutzer zu administrieren, Einstellungen und alle Einträge zu verändern', - 'user_create_all' => 'Klienten, Rechnungen, etc. erstellen', - 'user_view_all' => 'Klienten, Rechnungen, etc. ansehen', - 'user_edit_all' => 'Klienten, Rechnungen, etc. bearbeiten', + 'user_create_all' => 'Kunden, Rechnungen, etc. erstellen', + 'user_view_all' => 'Kunden, Rechnungen, etc. ansehen', + 'user_edit_all' => 'Kunden, Rechnungen, etc. bearbeiten', 'gateway_help_20' => ':link um sich bei Sage Pay zu registrieren.', 'gateway_help_21' => ':link um sich bei Sage Pay zu registrieren.', 'partial_due' => 'Offener Teilbetrag', From a24ae57b4eeeb4b61290c64afefe871c5db58cd0 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sat, 30 Apr 2016 21:41:05 +0300 Subject: [PATCH 025/120] Fix for travis tests --- tests/acceptance/InvoiceCest.php | 2 +- tests/acceptance/TaxRatesCest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/acceptance/InvoiceCest.php b/tests/acceptance/InvoiceCest.php index 2d8b818d161d..07ac60e24215 100644 --- a/tests/acceptance/InvoiceCest.php +++ b/tests/acceptance/InvoiceCest.php @@ -64,7 +64,7 @@ class InvoiceCest $this->fillItems($I); $I->executeJS("submitAction('email')"); - $I->wait(1); + $I->wait(2); $I->see($clientEmail); $invoiceNumber = $I->grabAttributeFrom('#invoice_number', 'value'); diff --git a/tests/acceptance/TaxRatesCest.php b/tests/acceptance/TaxRatesCest.php index 75f7de7a7508..ebe68f7b5880 100644 --- a/tests/acceptance/TaxRatesCest.php +++ b/tests/acceptance/TaxRatesCest.php @@ -78,7 +78,7 @@ class TaxRatesCest // check total is right before saving $I->see("\${$total}"); $I->click('Save'); - $I->wait(2); + $I->wait(3); $I->see($clientEmail); // check total is right after saving From a9e10985a189e22f220864b83f9a71b89b09f59b Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sat, 30 Apr 2016 21:52:53 +0300 Subject: [PATCH 026/120] Fix for tests --- tests/acceptance/CheckBalanceCest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/acceptance/CheckBalanceCest.php b/tests/acceptance/CheckBalanceCest.php index 33378baab0ba..68cae45c3154 100644 --- a/tests/acceptance/CheckBalanceCest.php +++ b/tests/acceptance/CheckBalanceCest.php @@ -46,7 +46,7 @@ class CheckBalanceCest $I->fillField('table.invoice-table tbody tr:nth-child(1) #product_key', $productKey); $I->click('table.invoice-table tbody tr:nth-child(1) .tt-selectable'); $I->click('Save'); - $I->wait(1); + $I->wait(2); $I->see($clientEmail); $invoiceId = $I->grabFromCurrentUrl('~invoices/(\d+)~'); $I->amOnPage("/clients/{$clientId}"); From 50de2d757edef8657da85f91f4f05e4489d15d14 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sat, 30 Apr 2016 21:55:15 +0300 Subject: [PATCH 027/120] Working on permissions --- app/Http/Controllers/ClientController.php | 7 ++----- app/Http/Requests/ClientRequest.php | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/ClientController.php b/app/Http/Controllers/ClientController.php index 37786b401b53..ee400287b4f1 100644 --- a/app/Http/Controllers/ClientController.php +++ b/app/Http/Controllers/ClientController.php @@ -95,12 +95,9 @@ class ClientController extends BaseController * @param int $id * @return Response */ - public function show($publicId) + public function show(ClientRequest $request) { - //$client = $request->entity()->load('conacts'); - $client = Client::withTrashed()->scope($publicId)->with('contacts', 'size', 'industry')->firstOrFail(); - - $this->authorize('view', $client); + $client = $request->entity(); $user = Auth::user(); Utils::trackViewed($client->getDisplayName(), ENTITY_CLIENT); diff --git a/app/Http/Requests/ClientRequest.php b/app/Http/Requests/ClientRequest.php index e6470c6bd621..9d8e51556c9e 100644 --- a/app/Http/Requests/ClientRequest.php +++ b/app/Http/Requests/ClientRequest.php @@ -11,7 +11,7 @@ class ClientRequest extends BaseRequest { public function authorize() { - return true; + return $this->user()->can('view', $this->entity()); } /** From 1000ff40887079550c1f29804942a0c0fb0e6a2b Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 08:55:59 +0300 Subject: [PATCH 028/120] Fix for currency formatting on invoice --- app/Models/Invoice.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index c0da6b8faff6..deb65b75ab30 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -436,6 +436,7 @@ class Invoice extends EntityModel implements BalanceAffecting 'contacts', 'country', 'currency_id', + 'country_id', 'custom_value1', 'custom_value2', ]); From 2e2470e11ced5b02cb6ca32155840a03d1f183d4 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 09:43:41 +0300 Subject: [PATCH 029/120] Send contact's email when creating Stripe token --- app/Services/PaymentService.php | 2 +- resources/views/payments/payment.blade.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Services/PaymentService.php b/app/Services/PaymentService.php index 9b325bd4e447..23b5f4e8f167 100644 --- a/app/Services/PaymentService.php +++ b/app/Services/PaymentService.php @@ -63,7 +63,6 @@ class PaymentService extends BaseService if ($input) { $data = self::convertInputForOmnipay($input); - $data['email'] = $invitation->contact->email; Session::put($key, $data); } elseif (Session::get($key)) { $data = Session::get($key); @@ -95,6 +94,7 @@ class PaymentService extends BaseService $data = [ 'firstName' => $input['first_name'], 'lastName' => $input['last_name'], + 'email' => $input['email'], 'number' => isset($input['card_number']) ? $input['card_number'] : null, 'expiryMonth' => isset($input['expiration_month']) ? $input['expiration_month'] : null, 'expiryYear' => isset($input['expiration_year']) ? $input['expiration_year'] : null, diff --git a/resources/views/payments/payment.blade.php b/resources/views/payments/payment.blade.php index 274361938bac..949723e7f17a 100644 --- a/resources/views/payments/payment.blade.php +++ b/resources/views/payments/payment.blade.php @@ -13,6 +13,7 @@ var data = { name: $('#first_name').val() + ' ' + $('#last_name').val(), + email: $('#email').val(), address_line1: $('#address1').val(), address_line2: $('#address2').val(), address_city: $('#city').val(), @@ -117,6 +118,7 @@ {{ Former::populate($client) }} {{ Former::populateField('first_name', $contact->first_name) }} {{ Former::populateField('last_name', $contact->last_name) }} + {{ Former::populateField('email', $contact->email) }} @if (!$client->country_id && $client->account->country_id) {{ Former::populateField('country_id', $client->account->country_id) }} @endif @@ -178,8 +180,7 @@ ->label('') !!}
- @if (isset($paymentTitle)) -
+
{!! Former::text('email') ->placeholder(trans('texts.email')) @@ -187,7 +188,6 @@ ->label('') !!}
- @endif

 
 

From 1647effb20ffd34645394ef6346e795dfcb1dde2 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 11:43:10 +0300 Subject: [PATCH 030/120] Fix for client portal invoice table --- app/Ninja/Repositories/InvoiceRepository.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index 15616e9cd75f..aebb177b5852 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -153,11 +153,14 @@ class InvoiceRepository extends BaseRepository ->join('accounts', 'accounts.id', '=', 'invitations.account_id') ->join('invoices', 'invoices.id', '=', 'invitations.invoice_id') ->join('clients', 'clients.id', '=', 'invoices.client_id') + ->join('contacts', 'contacts.client_id', '=', 'clients.id') ->where('invitations.contact_id', '=', $contactId) ->where('invitations.deleted_at', '=', null) ->where('invoices.is_quote', '=', $entityType == ENTITY_QUOTE) ->where('invoices.is_deleted', '=', false) ->where('clients.deleted_at', '=', null) + ->where('contacts.deleted_at', '=', null) + ->where('contacts.is_primary', '=', true) ->where('invoices.is_recurring', '=', false) // This needs to be a setting to also hide the activity on the dashboard page //->where('invoices.invoice_status_id', '>=', INVOICE_STATUS_SENT) From c144a1c604b72008644f9ca676645e0e1cec04e4 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 11:43:30 +0300 Subject: [PATCH 031/120] Fix to make it easier to test features --- app/Models/Account.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/Models/Account.php b/app/Models/Account.php index e2e7d2f8a002..c8ece3b4da95 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -796,6 +796,10 @@ class Account extends Eloquent public function hasFeature($feature) { + if (Utils::isNinjaDev()) { + return true; + } + $planDetails = $this->getPlanDetails(); $selfHost = !Utils::isNinjaProd(); From 05bd8d93188f3f887ff520ac1f0043e4c1cb4eb7 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 14:31:10 +0300 Subject: [PATCH 032/120] Moving permissions to form requests --- app/Commands/Command.php | 2 +- app/Console/Commands/GenerateResources.php | 2 +- app/Console/Commands/TestOFX.php | 2 +- app/Console/Kernel.php | 2 +- app/Http/Controllers/ClientController.php | 8 +--- app/Http/Controllers/CreditController.php | 16 +++---- app/Http/Controllers/ExpenseController.php | 44 +++++++------------ app/Http/Controllers/ImportController.php | 2 +- app/Http/Controllers/PaymentController.php | 29 +++++------- app/Http/Controllers/TaskController.php | 2 +- app/Http/Controllers/VendorController.php | 35 +++++---------- .../Middleware/DuplicateSubmissionCheck.php | 2 +- app/Http/Middleware/StartupCheck.php | 2 +- app/Http/Requests/ClientRequest.php | 28 +++++------- .../Requests/CreateBankAccountRequest.php | 2 +- app/Http/Requests/CreateCreditRequest.php | 2 +- app/Http/Requests/CreateExpenseRequest.php | 9 ++-- app/Http/Requests/CreateInvoiceRequest.php | 2 +- app/Http/Requests/CreatePaymentRequest.php | 8 ++-- .../Requests/CreatePaymentTermRequest.php | 2 +- app/Http/Requests/CreateTaxRateRequest.php | 2 +- app/Http/Requests/CreateVendorRequest.php | 9 ++-- .../{BaseRequest.php => EntityRequest.php} | 18 +++++++- app/Http/Requests/RegisterRequest.php | 2 +- .../Requests/SaveInvoiceWithClientRequest.php | 2 +- app/Http/Requests/UpdateAccountRequest.php | 2 +- app/Http/Requests/UpdateExpenseRequest.php | 10 ++--- app/Http/Requests/UpdateInvoiceRequest.php | 2 +- app/Http/Requests/UpdatePaymentRequest.php | 9 ++-- .../Requests/UpdatePaymentTermRequest.php | 2 +- app/Http/Requests/UpdateTaxRateRequest.php | 2 +- app/Http/Requests/UpdateUserRequest.php | 2 +- app/Http/Requests/UpdateVendorRequest.php | 9 ++-- app/Listeners/ActivityListener.php | 2 +- app/Listeners/CreditListener.php | 2 +- app/Listeners/ExpenseListener.php | 2 +- app/Listeners/HandleUserSignedUp.php | 2 +- app/Listeners/InvoiceListener.php | 2 +- app/Listeners/NotificationListener.php | 2 +- app/Listeners/QuoteListener.php | 2 +- app/Listeners/SubscriptionListener.php | 2 +- app/Listeners/TaskListener.php | 2 +- app/Models/InvoiceDesign.php | 2 +- app/Ninja/Repositories/DocumentRepository.php | 2 +- app/Ninja/Repositories/ExpenseRepository.php | 2 +- app/Ninja/Repositories/InvoiceRepository.php | 2 +- 46 files changed, 122 insertions(+), 176 deletions(-) rename app/Http/Requests/{BaseRequest.php => EntityRequest.php} (66%) diff --git a/app/Commands/Command.php b/app/Commands/Command.php index 5bc48501167e..d6a8d61150ae 100644 --- a/app/Commands/Command.php +++ b/app/Commands/Command.php @@ -1,4 +1,4 @@ -authorizeCreate(); - if (Client::scope()->withTrashed()->count() > Auth::user()->getMaxNumClients()) { return View::make('error', ['hideHeader' => true, 'error' => "Sorry, you've exceeded the limit of ".Auth::user()->getMaxNumClients()." clients"]); } @@ -175,9 +173,7 @@ class ClientController extends BaseController public function edit(ClientRequest $request) { $client = $request->entity(); - - $this->authorize('edit', $client); - + $data = [ 'client' => $client, 'method' => 'PUT', diff --git a/app/Http/Controllers/CreditController.php b/app/Http/Controllers/CreditController.php index 1e3586af1922..c4250903fd32 100644 --- a/app/Http/Controllers/CreditController.php +++ b/app/Http/Controllers/CreditController.php @@ -12,6 +12,7 @@ use App\Models\Client; use App\Services\CreditService; use App\Ninja\Repositories\CreditRepository; use App\Http\Requests\CreateCreditRequest; +use App\Http\Requests\CreditRequest; class CreditController extends BaseController { @@ -55,23 +56,21 @@ class CreditController extends BaseController return $this->creditService->getDatatable($clientPublicId, Input::get('sSearch')); } - public function create($clientPublicId = 0) + public function create(CreditRequest $request) { - $this->authorizeCreate(); - $data = array( - 'clientPublicId' => Input::old('client') ? Input::old('client') : $clientPublicId, - //'invoicePublicId' => Input::old('invoice') ? Input::old('invoice') : $invoicePublicId, + 'clientPublicId' => Input::old('client') ? Input::old('client') : ($request->client_id ?: 0), 'credit' => null, 'method' => 'POST', 'url' => 'credits', 'title' => trans('texts.new_credit'), - //'invoices' => Invoice::scope()->with('client', 'invoice_status')->orderBy('invoice_number')->get(), - 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), ); + 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), + ); return View::make('credits.edit', $data); } + /* public function edit($publicId) { $credit = Credit::scope($publicId)->firstOrFail(); @@ -90,7 +89,8 @@ class CreditController extends BaseController return View::make('credit.edit', $data); } - + */ + public function store(CreateCreditRequest $request) { $credit = $this->creditRepo->save($request->input()); diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index 88a493d3ea21..406613489c6b 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -17,6 +17,8 @@ use App\Models\Expense; use App\Models\Client; use App\Services\ExpenseService; use App\Ninja\Repositories\ExpenseRepository; + +use App\Http\Requests\ExpenseRequest; use App\Http\Requests\CreateExpenseRequest; use App\Http\Requests\UpdateExpenseRequest; @@ -69,17 +71,16 @@ class ExpenseController extends BaseController return $this->expenseService->getDatatableVendor($vendorPublicId); } - public function create($vendorPublicId = null, $clientPublicId = null) + public function create(ExpenseRequest $request) { - $this->authorizeCreate(); - - if($vendorPublicId != 0) { - $vendor = Vendor::scope($vendorPublicId)->with('vendorcontacts')->firstOrFail(); + if ($request->vendor_id != 0) { + $vendor = Vendor::scope($request->vendor_id)->with('vendorcontacts')->firstOrFail(); } else { $vendor = null; } + $data = array( - 'vendorPublicId' => Input::old('vendor') ? Input::old('vendor') : $vendorPublicId, + 'vendorPublicId' => Input::old('vendor') ? Input::old('vendor') : $request->vendor_id, 'expense' => null, 'method' => 'POST', 'url' => 'expenses', @@ -87,20 +88,18 @@ class ExpenseController extends BaseController 'vendors' => Vendor::scope()->with('vendorcontacts')->orderBy('name')->get(), 'vendor' => $vendor, 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), - 'clientPublicId' => $clientPublicId, - ); + 'clientPublicId' => $request->client_id, + ); $data = array_merge($data, self::getViewModel()); return View::make('expenses.edit', $data); } - public function edit($publicId) + public function edit(ExpenseRequest $request) { - $expense = Expense::scope($publicId)->with('documents')->firstOrFail(); - - $this->authorize('edit', $expense); - + $expense = $request->entity(); + $expense->expense_date = Utils::fromSqlDate($expense->expense_date); $actions = []; @@ -108,15 +107,6 @@ class ExpenseController extends BaseController $actions[] = ['url' => URL::to("invoices/{$expense->invoice->public_id}/edit"), 'label' => trans("texts.view_invoice")]; } else { $actions[] = ['url' => 'javascript:submitAction("invoice")', 'label' => trans("texts.invoice_expense")]; - - /* - // check for any open invoices - $invoices = $task->client_id ? $this->invoiceRepo->findOpenInvoices($task->client_id) : []; - - foreach ($invoices as $invoice) { - $actions[] = ['url' => 'javascript:submitAction("add_to_invoice", '.$invoice->public_id.')', 'label' => trans("texts.add_to_invoice", ["invoice" => $invoice->invoice_number])]; - } - */ } $actions[] = \DropdownButton::DIVIDER; @@ -131,7 +121,7 @@ class ExpenseController extends BaseController 'vendor' => null, 'expense' => $expense, 'method' => 'PUT', - 'url' => 'expenses/'.$publicId, + 'url' => 'expenses/'.$expense->public_id, 'title' => 'Edit Expense', 'actions' => $actions, 'vendors' => Vendor::scope()->with('vendorcontacts')->orderBy('name')->get(), @@ -155,9 +145,7 @@ class ExpenseController extends BaseController { $data = $request->input(); $data['documents'] = $request->file('documents'); - - $this->authorizeUpdate($data); - + $expense = $this->expenseService->save($data); Session::flash('message', trans('texts.updated_expense')); @@ -174,9 +162,7 @@ class ExpenseController extends BaseController { $data = $request->input(); $data['documents'] = $request->file('documents'); - - $this->authorizeUpdate($data); - + $expense = $this->expenseService->save($data); Session::flash('message', trans('texts.created_expense')); diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index 38618475064b..fe006332ca48 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -1,4 +1,4 @@ -paymentService->getDatatable($clientPublicId, Input::get('sSearch')); } - public function create($clientPublicId = 0, $invoicePublicId = 0) + public function create(PaymentRequest $request) { - $this->authorizeCreate(); - $invoices = Invoice::scope() ->where('is_recurring', '=', false) ->where('is_quote', '=', false) @@ -79,8 +78,8 @@ class PaymentController extends BaseController ->orderBy('invoice_number')->get(); $data = array( - 'clientPublicId' => Input::old('client') ? Input::old('client') : $clientPublicId, - 'invoicePublicId' => Input::old('invoice') ? Input::old('invoice') : $invoicePublicId, + 'clientPublicId' => Input::old('client') ? Input::old('client') : ($request->client_id ?: 0), + 'invoicePublicId' => Input::old('invoice') ? Input::old('invoice') : ($request->invoice_id ?: 0), 'invoice' => null, 'invoices' => $invoices, 'payment' => null, @@ -94,12 +93,10 @@ class PaymentController extends BaseController return View::make('payments.edit', $data); } - public function edit($publicId) + public function edit(PaymentRequest $request) { - $payment = Payment::scope($publicId)->firstOrFail(); - - $this->authorize('edit', $payment); - + $payment = $request->entity(); + $payment->payment_date = Utils::fromSqlDate($payment->payment_date); $data = array( @@ -109,7 +106,7 @@ class PaymentController extends BaseController ->with('client', 'invoice_status')->orderBy('invoice_number')->get(), 'payment' => $payment, 'method' => 'PUT', - 'url' => 'payments/'.$publicId, + 'url' => 'payments/'.$payment->public_id, 'title' => trans('texts.edit_payment'), 'paymentTypes' => Cache::get('paymentTypes'), 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), ); @@ -589,9 +586,7 @@ class PaymentController extends BaseController public function store(CreatePaymentRequest $request) { $input = $request->input(); - - $this->authorizeUpdate($input); - + $input['invoice_id'] = Invoice::getPrivateId($input['invoice']); $input['client_id'] = Client::getPrivateId($input['client']); $payment = $this->paymentRepo->save($input); @@ -608,11 +603,7 @@ class PaymentController extends BaseController public function update(UpdatePaymentRequest $request) { - $input = $request->input(); - - $this->authorizeUpdate($input); - - $payment = $this->paymentRepo->save($input); + $payment = $this->paymentRepo->save($request->input()); Session::flash('message', trans('texts.updated_payment')); diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index a1118269ac51..565cb40955a9 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -90,7 +90,7 @@ class TaskController extends BaseController $data = [ 'task' => null, - 'clientPublicId' => Input::old('client') ? Input::old('client') : $clientPublicId, + 'clientPublicId' => Input::old('client') ? Input::old('client') : ($clientPublicId ?: 0), 'method' => 'POST', 'url' => 'tasks', 'title' => trans('texts.new_task'), diff --git a/app/Http/Controllers/VendorController.php b/app/Http/Controllers/VendorController.php index 9340b926d4eb..10c7f7f03e89 100644 --- a/app/Http/Controllers/VendorController.php +++ b/app/Http/Controllers/VendorController.php @@ -23,6 +23,7 @@ use App\Models\Country; use App\Ninja\Repositories\VendorRepository; use App\Services\VendorService; +use App\Http\Requests\VendorRequest; use App\Http\Requests\CreateVendorRequest; use App\Http\Requests\UpdateVendorRequest; @@ -38,8 +39,6 @@ class VendorController extends BaseController $this->vendorRepo = $vendorRepo; $this->vendorService = $vendorService; - - } /** @@ -77,11 +76,7 @@ class VendorController extends BaseController */ public function store(CreateVendorRequest $request) { - $data = $request->input(); - - $this->authorizeUpdate($data); - - $vendor = $this->vendorService->save($data); + $vendor = $this->vendorService->save($request->input()); Session::flash('message', trans('texts.created_vendor')); @@ -94,12 +89,10 @@ class VendorController extends BaseController * @param int $id * @return Response */ - public function show($publicId) + public function show(VendorRequest $request) { - $vendor = Vendor::withTrashed()->scope($publicId)->with('vendorcontacts', 'size', 'industry')->firstOrFail(); - - $this->authorize('view', $vendor); - + $vendor = $request->entity(); + Utils::trackViewed($vendor->getDisplayName(), 'vendor'); $actionLinks = [ @@ -125,10 +118,8 @@ class VendorController extends BaseController * * @return Response */ - public function create() + public function create(VendorRequest $request) { - $this->authorizeCreate(); - if (Vendor::scope()->count() > Auth::user()->getMaxNumVendors()) { return View::make('error', ['hideHeader' => true, 'error' => "Sorry, you've exceeded the limit of ".Auth::user()->getMaxNumVendors()." vendors"]); } @@ -151,16 +142,14 @@ class VendorController extends BaseController * @param int $id * @return Response */ - public function edit($publicId) + public function edit(VendorRequest $request) { - $vendor = Vendor::scope($publicId)->with('vendorcontacts')->firstOrFail(); - - $this->authorize('edit', $vendor); + $vendor = $request->entity(); $data = [ 'vendor' => $vendor, 'method' => 'PUT', - 'url' => 'vendors/'.$publicId, + 'url' => 'vendors/'.$vendor->public_id, 'title' => trans('texts.edit_vendor'), ]; @@ -193,11 +182,7 @@ class VendorController extends BaseController */ public function update(UpdateVendorRequest $request) { - $data = $request->input(); - - $this->authorizeUpdate($data); - - $vendor = $this->vendorService->save($data); + $vendor = $this->vendorService->save($request->input()); Session::flash('message', trans('texts.updated_vendor')); diff --git a/app/Http/Middleware/DuplicateSubmissionCheck.php b/app/Http/Middleware/DuplicateSubmissionCheck.php index 407ffab60071..6f3374a47ebf 100644 --- a/app/Http/Middleware/DuplicateSubmissionCheck.php +++ b/app/Http/Middleware/DuplicateSubmissionCheck.php @@ -1,4 +1,4 @@ -load('contacts'); + $client = parent::entity(); + + // eager load the contacts + if ($client && ! count($client->contacts)) { + $client->load('contacts'); + } + + return $client; } - - public function authorize() - { - return $this->user()->can('view', $this->entity()); - } - - /** - * Get the validation rules that apply to the request. - * - * @return array - */ - public function rules() - { - return []; - } -} +} \ No newline at end of file diff --git a/app/Http/Requests/CreateBankAccountRequest.php b/app/Http/Requests/CreateBankAccountRequest.php index 6c2fea62ec47..eac988349c8d 100644 --- a/app/Http/Requests/CreateBankAccountRequest.php +++ b/app/Http/Requests/CreateBankAccountRequest.php @@ -1,4 +1,4 @@ -user()->can('create', ENTITY_EXPENSE); } /** diff --git a/app/Http/Requests/CreateInvoiceRequest.php b/app/Http/Requests/CreateInvoiceRequest.php index 4a11ea56044a..8531a4074879 100644 --- a/app/Http/Requests/CreateInvoiceRequest.php +++ b/app/Http/Requests/CreateInvoiceRequest.php @@ -1,4 +1,4 @@ -user()->can('create', ENTITY_PAYMENT); } /** diff --git a/app/Http/Requests/CreatePaymentTermRequest.php b/app/Http/Requests/CreatePaymentTermRequest.php index d8581793160e..23bf3151d096 100644 --- a/app/Http/Requests/CreatePaymentTermRequest.php +++ b/app/Http/Requests/CreatePaymentTermRequest.php @@ -1,4 +1,4 @@ -user()->can('create', ENTITY_VENDOR); } /** diff --git a/app/Http/Requests/BaseRequest.php b/app/Http/Requests/EntityRequest.php similarity index 66% rename from app/Http/Requests/BaseRequest.php rename to app/Http/Requests/EntityRequest.php index eb97a949a8f3..53013004a4f9 100644 --- a/app/Http/Requests/BaseRequest.php +++ b/app/Http/Requests/EntityRequest.php @@ -4,7 +4,7 @@ use App\Http\Requests\Request; use Input; use Utils; -class BaseRequest extends Request { +class EntityRequest extends Request { protected $entityType; private $entity; @@ -14,7 +14,7 @@ class BaseRequest extends Request { if ($this->entity) { return $this->entity; } - + $paramName = $this->entityType . 's'; $publicId = $this->$paramName ?: (Input::get('public_id') ?: Input::get('id')); @@ -27,4 +27,18 @@ class BaseRequest extends Request { return $this->entity; } + + public function authorize() + { + if ($this->entity()) { + return $this->user()->can('view', $this->entity()); + } else { + return $this->user()->can('create', $this->entityType); + } + } + + public function rules() + { + return []; + } } diff --git a/app/Http/Requests/RegisterRequest.php b/app/Http/Requests/RegisterRequest.php index 91a27556924c..5c117fc23a09 100644 --- a/app/Http/Requests/RegisterRequest.php +++ b/app/Http/Requests/RegisterRequest.php @@ -1,4 +1,4 @@ -user()->can('edit', $this->entity()); } /** diff --git a/app/Http/Requests/UpdateInvoiceRequest.php b/app/Http/Requests/UpdateInvoiceRequest.php index 4b32bf4ccead..7827e2b1d1e1 100644 --- a/app/Http/Requests/UpdateInvoiceRequest.php +++ b/app/Http/Requests/UpdateInvoiceRequest.php @@ -1,4 +1,4 @@ -user()->can('edit', $this->entity()); } /** diff --git a/app/Http/Requests/UpdatePaymentTermRequest.php b/app/Http/Requests/UpdatePaymentTermRequest.php index b3d4f536bc6e..ea9ff80e9772 100644 --- a/app/Http/Requests/UpdatePaymentTermRequest.php +++ b/app/Http/Requests/UpdatePaymentTermRequest.php @@ -1,4 +1,4 @@ -user()->can('edit', $this->entity()); } /** diff --git a/app/Listeners/ActivityListener.php b/app/Listeners/ActivityListener.php index 52c2e26f9027..7edd065f915d 100644 --- a/app/Listeners/ActivityListener.php +++ b/app/Listeners/ActivityListener.php @@ -1,4 +1,4 @@ - Date: Sun, 1 May 2016 15:04:55 +0300 Subject: [PATCH 033/120] Moving permissions to form requests --- app/Http/Controllers/TaskController.php | 39 ++++++++++------------- app/Http/Requests/CreateCreditRequest.php | 2 +- app/Http/Requests/CreateTaskRequest.php | 26 +++++++++++++++ app/Http/Requests/CreditRequest.php | 7 ++++ app/Http/Requests/ExpenseRequest.php | 18 +++++++++++ app/Http/Requests/PaymentRequest.php | 7 ++++ app/Http/Requests/TaskRequest.php | 7 ++++ app/Http/Requests/UpdateTaskRequest.php | 26 +++++++++++++++ app/Http/Requests/VendorRequest.php | 19 +++++++++++ app/Ninja/Repositories/TaskRepository.php | 14 -------- resources/views/tasks/edit.blade.php | 6 +++- 11 files changed, 132 insertions(+), 39 deletions(-) create mode 100644 app/Http/Requests/CreateTaskRequest.php create mode 100644 app/Http/Requests/CreditRequest.php create mode 100644 app/Http/Requests/ExpenseRequest.php create mode 100644 app/Http/Requests/PaymentRequest.php create mode 100644 app/Http/Requests/TaskRequest.php create mode 100644 app/Http/Requests/UpdateTaskRequest.php create mode 100644 app/Http/Requests/VendorRequest.php diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index 565cb40955a9..e78462cd51ac 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -18,6 +18,10 @@ use App\Ninja\Repositories\TaskRepository; use App\Ninja\Repositories\InvoiceRepository; use App\Services\TaskService; +use App\Http\Requests\TaskRequest; +use App\Http\Requests\CreateTaskRequest; +use App\Http\Requests\UpdateTaskRequest; + class TaskController extends BaseController { protected $taskRepo; @@ -66,16 +70,16 @@ class TaskController extends BaseController * * @return Response */ - public function store() + public function store(CreateTaskRequest $request) { return $this->save(); } - public function show($publicId) + public function show(TaskRequest $request) { Session::reflash(); - return Redirect::to("tasks/{$publicId}/edit"); + return Redirect::to("tasks/{$request->task_id}/edit"); } /** @@ -83,14 +87,13 @@ class TaskController extends BaseController * * @return Response */ - public function create($clientPublicId = 0) + public function create(TaskRequest $request) { - $this->authorizeCreate(); $this->checkTimezone(); $data = [ 'task' => null, - 'clientPublicId' => Input::old('client') ? Input::old('client') : ($clientPublicId ?: 0), + 'clientPublicId' => Input::old('client') ? Input::old('client') : ($request->client_id ?: 0), 'method' => 'POST', 'url' => 'tasks', 'title' => trans('texts.new_task'), @@ -109,13 +112,11 @@ class TaskController extends BaseController * @param int $id * @return Response */ - public function edit($publicId) + public function edit(TaskRequest $request) { $this->checkTimezone(); - $task = Task::scope($publicId)->with('client', 'invoice')->withTrashed()->firstOrFail(); - - $this->authorize('edit', $task); + $task = $request->entity(); $actions = []; if ($task->invoice) { @@ -143,7 +144,7 @@ class TaskController extends BaseController 'task' => $task, 'clientPublicId' => $task->client ? $task->client->public_id : 0, 'method' => 'PUT', - 'url' => 'tasks/'.$publicId, + 'url' => 'tasks/'.$task->public_id, 'title' => trans('texts.edit_task'), 'duration' => $task->is_running ? $task->getCurrentDuration() : $task->getDuration(), 'actions' => $actions, @@ -163,9 +164,11 @@ class TaskController extends BaseController * @param int $id * @return Response */ - public function update($publicId) + public function update(UpdateTaskRequest $request) { - return $this->save($publicId); + $task = $request->entity(); + + return $this->save($task->public_id); } private static function getViewModel() @@ -180,20 +183,10 @@ class TaskController extends BaseController { $action = Input::get('action'); - $this->authorizeUpdate(array('public_id'=>$publicId)/* Hacky, but works */); - if (in_array($action, ['archive', 'delete', 'restore'])) { return self::bulk(); } - if ($validator = $this->taskRepo->getErrors(Input::all())) { - $url = $publicId ? 'tasks/'.$publicId.'/edit' : 'tasks/create'; - Session::flash('error', trans('texts.task_errors')); - return Redirect::to($url) - ->withErrors($validator) - ->withInput(); - } - $task = $this->taskRepo->save($publicId, Input::all()); Session::flash('message', trans($publicId ? 'texts.updated_task' : 'texts.created_task')); diff --git a/app/Http/Requests/CreateCreditRequest.php b/app/Http/Requests/CreateCreditRequest.php index 58d509c69b6d..b6f4fe3b37e3 100644 --- a/app/Http/Requests/CreateCreditRequest.php +++ b/app/Http/Requests/CreateCreditRequest.php @@ -1,6 +1,6 @@ user()->can('create', ENTITY_TASK); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + 'time_log' => 'time_log', + ]; + } +} diff --git a/app/Http/Requests/CreditRequest.php b/app/Http/Requests/CreditRequest.php new file mode 100644 index 000000000000..7968005555f4 --- /dev/null +++ b/app/Http/Requests/CreditRequest.php @@ -0,0 +1,7 @@ +documents)) { + $expense->load('documents'); + } + + return $expense; + } +} \ No newline at end of file diff --git a/app/Http/Requests/PaymentRequest.php b/app/Http/Requests/PaymentRequest.php new file mode 100644 index 000000000000..cb34349f5d20 --- /dev/null +++ b/app/Http/Requests/PaymentRequest.php @@ -0,0 +1,7 @@ +user()->can('edit', $this->entity()); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + 'time_log' => 'time_log', + ]; + } +} diff --git a/app/Http/Requests/VendorRequest.php b/app/Http/Requests/VendorRequest.php new file mode 100644 index 000000000000..600678686c0e --- /dev/null +++ b/app/Http/Requests/VendorRequest.php @@ -0,0 +1,19 @@ +vendorcontacts)) { + $vendor->load('vendorcontacts'); + } + + return $vendor; + } + +} \ No newline at end of file diff --git a/app/Ninja/Repositories/TaskRepository.php b/app/Ninja/Repositories/TaskRepository.php index d78b53c02d56..1bd0e38cb6b7 100644 --- a/app/Ninja/Repositories/TaskRepository.php +++ b/app/Ninja/Repositories/TaskRepository.php @@ -64,20 +64,6 @@ class TaskRepository return $query; } - public function getErrors($input) - { - $rules = [ - 'time_log' => 'time_log', - ]; - $validator = \Validator::make($input, $rules); - - if ($validator->fails()) { - return $validator; - } - - return false; - } - public function save($publicId, $data) { if ($publicId) { diff --git a/resources/views/tasks/edit.blade.php b/resources/views/tasks/edit.blade.php index fb3648206c6c..15c593b905b4 100644 --- a/resources/views/tasks/edit.blade.php +++ b/resources/views/tasks/edit.blade.php @@ -114,6 +114,10 @@
+ @if ($errors->first('time_log')) +
  • {{ trans('texts.task_errors') }}
  • + @endif + @@ -455,7 +459,7 @@ @endif @endif - @if (Session::has('error')) + @if ($errors->first('time_log')) loadTimeLog({!! json_encode(Input::old('time_log')) !!}); model.showTimeOverlaps(); showTimeDetails(); From 65c8ef40786eb65477b1e5568584acb6725921f5 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 15:35:51 +0300 Subject: [PATCH 034/120] Moving permissions to form requests --- resources/views/tasks/edit.blade.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/resources/views/tasks/edit.blade.php b/resources/views/tasks/edit.blade.php index 15c593b905b4..0a42f343b6bf 100644 --- a/resources/views/tasks/edit.blade.php +++ b/resources/views/tasks/edit.blade.php @@ -19,6 +19,11 @@ + @if ($errors->first('time_log')) +
  • {{ trans('texts.task_errors') }}
  • + @endif + + {!! Former::open($url)->addClass('col-md-10 col-md-offset-1 warn-on-exit task-form')->method($method)->rules(array()) !!} @if ($task) {!! Former::populate($task) !!} @@ -114,10 +119,6 @@ - @if ($errors->first('time_log')) -
  • {{ trans('texts.task_errors') }}
  • - @endif - From 933f2968d65388f382d7435e1bd7bff59909f218 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 22:30:39 +0300 Subject: [PATCH 035/120] Working on form requests --- app/Http/Controllers/DocumentController.php | 31 +++++-------- app/Http/Controllers/InvoiceApiController.php | 8 ++-- app/Http/Controllers/InvoiceController.php | 45 +++++++++---------- app/Http/Controllers/QuoteController.php | 5 +-- app/Http/Controllers/TaskController.php | 4 +- app/Http/Requests/CreateDocumentRequest.php | 26 +++++++++++ app/Http/Requests/CreateInvoiceAPIRequest.php | 32 +++++++++++++ app/Http/Requests/CreateInvoiceRequest.php | 20 ++++----- app/Http/Requests/DocumentRequest.php | 7 +++ app/Http/Requests/EntityRequest.php | 7 ++- app/Http/Requests/InvoiceRequest.php | 20 +++++++++ .../Requests/SaveInvoiceWithClientRequest.php | 45 ------------------- app/Http/Requests/UpdateInvoiceAPIRequest.php | 36 +++++++++++++++ app/Http/Requests/UpdateInvoiceRequest.php | 27 +++++------ app/Http/routes.php | 20 ++++----- app/Models/Document.php | 10 ++--- app/Ninja/Repositories/DocumentRepository.php | 2 +- 17 files changed, 205 insertions(+), 140 deletions(-) create mode 100644 app/Http/Requests/CreateDocumentRequest.php create mode 100644 app/Http/Requests/CreateInvoiceAPIRequest.php create mode 100644 app/Http/Requests/DocumentRequest.php create mode 100644 app/Http/Requests/InvoiceRequest.php delete mode 100644 app/Http/Requests/SaveInvoiceWithClientRequest.php create mode 100644 app/Http/Requests/UpdateInvoiceAPIRequest.php diff --git a/app/Http/Controllers/DocumentController.php b/app/Http/Controllers/DocumentController.php index 0720e1a5babf..d597ba004474 100644 --- a/app/Http/Controllers/DocumentController.php +++ b/app/Http/Controllers/DocumentController.php @@ -12,6 +12,9 @@ use Response; use App\Models\Document; use App\Ninja\Repositories\DocumentRepository; +use App\Http\Requests\DocumentRequest; +use App\Http\Requests\CreateDocumentRequest; + class DocumentController extends BaseController { protected $documentRepo; @@ -24,14 +27,9 @@ class DocumentController extends BaseController $this->documentRepo = $documentRepo; } - public function get($publicId) + public function get(DocumentRequest $request) { - $document = Document::scope($publicId) - ->firstOrFail(); - - $this->authorize('view', $document); - - return static::getDownloadResponse($document); + return static::getDownloadResponse($request->entity()); } public static function getDownloadResponse($document){ @@ -60,12 +58,9 @@ class DocumentController extends BaseController return $response; } - public function getPreview($publicId) + public function getPreview(DocumentRequest $request) { - $document = Document::scope($publicId) - ->firstOrFail(); - - $this->authorize('view', $document); + $document = $request->entity(); if(empty($document->preview)){ return Response::view('error', array('error'=>'Preview does not exist!'), 404); @@ -83,16 +78,14 @@ class DocumentController extends BaseController return $response; } - public function getVFSJS($publicId, $name){ - $document = Document::scope($publicId) - ->firstOrFail(); + public function getVFSJS(DocumentRequest $request, $publicId, $name) + { + $document = $request->entity(); if(substr($name, -3)=='.js'){ $name = substr($name, 0, -3); } - $this->authorize('view', $document); - if(!$document->isPDFEmbeddable()){ return Response::view('error', array('error'=>'Image does not exist!'), 404); } @@ -106,14 +99,12 @@ class DocumentController extends BaseController return $response; } - public function postUpload() + public function postUpload(CreateDocumentRequest $request) { if (!Utils::hasFeature(FEATURE_DOCUMENTS)) { return; } - $this->authorizeCreate(); - $result = $this->documentRepo->upload(Input::all()['file'], $doc_array); if(is_string($result)){ diff --git a/app/Http/Controllers/InvoiceApiController.php b/app/Http/Controllers/InvoiceApiController.php index 53d2a2548e39..fab8aa3bfd3f 100644 --- a/app/Http/Controllers/InvoiceApiController.php +++ b/app/Http/Controllers/InvoiceApiController.php @@ -18,8 +18,8 @@ use App\Ninja\Repositories\InvoiceRepository; use App\Ninja\Mailers\ContactMailer as Mailer; use App\Http\Controllers\BaseAPIController; use App\Ninja\Transformers\InvoiceTransformer; -use App\Http\Requests\CreateInvoiceRequest; -use App\Http\Requests\UpdateInvoiceRequest; +use App\Http\Requests\CreateInvoiceAPIRequest; +use App\Http\Requests\UpdateInvoiceAPIRequest; use App\Services\InvoiceService; class InvoiceApiController extends BaseAPIController @@ -139,7 +139,7 @@ class InvoiceApiController extends BaseAPIController * ) * ) */ - public function store(CreateInvoiceRequest $request) + public function store(CreateInvoiceAPIRequest $request) { $data = Input::all(); $error = null; @@ -351,7 +351,7 @@ class InvoiceApiController extends BaseAPIController * ) * ) */ - public function update(UpdateInvoiceRequest $request, $publicId) + public function update(UpdateInvoiceAPIRequest $request, $publicId) { if ($request->action == ACTION_ARCHIVE) { $invoice = Invoice::scope($publicId)->firstOrFail(); diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index c3627a01c301..4dc60f8c7421 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -27,7 +27,10 @@ use App\Ninja\Repositories\ClientRepository; use App\Ninja\Repositories\DocumentRepository; use App\Services\InvoiceService; use App\Services\RecurringInvoiceService; -use App\Http\Requests\SaveInvoiceWithClientRequest; + +use App\Http\Requests\InvoiceRequest; +use App\Http\Requests\CreateInvoiceRequest; +use App\Http\Requests\UpdateInvoiceRequest; class InvoiceController extends BaseController { @@ -88,18 +91,13 @@ class InvoiceController extends BaseController return $this->recurringInvoiceService->getDatatable($accountId, $clientPublicId, ENTITY_RECURRING_INVOICE, $search); } - public function edit($publicId, $clone = false) + public function edit(InvoiceRequest $request, $publicId, $clone = false) { $account = Auth::user()->account; - $invoice = Invoice::scope($publicId) - ->with('invitations', 'account.country', 'client.contacts', 'client.country', 'invoice_items', 'documents', 'expenses', 'expenses.documents', 'payments') - ->withTrashed() - ->firstOrFail(); - - $this->authorize('edit', $invoice); + $invoice = $request->entity()->load('invitations', 'account.country', 'client.contacts', 'client.country', 'invoice_items', 'documents', 'expenses', 'expenses.documents', 'payments'); $entityType = $invoice->getEntityType(); - + $contactIds = DB::table('invitations') ->join('contacts', 'contacts.id', '=', 'invitations.contact_id') ->where('invitations.invoice_id', '=', $invoice->id) @@ -120,7 +118,7 @@ class InvoiceController extends BaseController } else { Utils::trackViewed($invoice->getDisplayName().' - '.$invoice->client->getDisplayName(), $invoice->getEntityType()); $method = 'PUT'; - $url = "{$entityType}s/{$publicId}"; + $url = "{$entityType}s/{$invoice->public_id}"; $clients->whereId($invoice->client_id); } @@ -229,28 +227,27 @@ class InvoiceController extends BaseController return View::make('invoices.edit', $data); } - public function create($clientPublicId = 0, $isRecurring = false) + public function create(InvoiceRequest $request, $clientPublicId = 0, $isRecurring = false) { - $this->authorizeCreate(); - $account = Auth::user()->account; + $entityType = $isRecurring ? ENTITY_RECURRING_INVOICE : ENTITY_INVOICE; $clientId = null; - if ($clientPublicId) { - $clientId = Client::getPrivateId($clientPublicId); + if ($request->client_id) { + $clientId = Client::getPrivateId($request->client_id); } $invoice = $account->createInvoice($entityType, $clientId); $invoice->public_id = 0; - if(Session::get('expenses')){ + if (Session::get('expenses')) { $invoice->expenses = Expense::scope(Session::get('expenses'))->with('documents')->get(); } $clients = Client::scope()->with('contacts', 'country')->orderBy('name'); - if(!Auth::user()->hasPermission('view_all')){ + if (!Auth::user()->hasPermission('view_all')) { $clients = $clients->where('clients.user_id', '=', Auth::user()->id); } @@ -267,9 +264,9 @@ class InvoiceController extends BaseController return View::make('invoices.edit', $data); } - public function createRecurring($clientPublicId = 0) + public function createRecurring(ClientRequest $request, $clientPublicId = 0) { - return self::create($clientPublicId, true); + return self::create($request, $clientPublicId, true); } private static function getViewModel($invoice) @@ -395,7 +392,7 @@ class InvoiceController extends BaseController * * @return Response */ - public function store(SaveInvoiceWithClientRequest $request) + public function store(CreateInvoiceRequest $request) { $data = $request->input(); $data['documents'] = $request->file('documents'); @@ -434,7 +431,7 @@ class InvoiceController extends BaseController * @param int $id * @return Response */ - public function update(SaveInvoiceWithClientRequest $request) + public function update(UpdateInvoiceRequest $request) { $data = $request->input(); $data['documents'] = $request->file('documents'); @@ -521,7 +518,7 @@ class InvoiceController extends BaseController { Session::reflash(); - return Redirect::to("invoices/{$publicId}/edit"); + return Redirect::to("invoices/$publicId/edit"); } /** @@ -558,9 +555,9 @@ class InvoiceController extends BaseController return Redirect::to('invoices/'.$clone->public_id); } - public function cloneInvoice($publicId) + public function cloneInvoice(InvoiceRequest $request, $publicId) { - return self::edit($publicId, true); + return self::edit($request, $publicId, true); } public function invoiceHistory($publicId) diff --git a/app/Http/Controllers/QuoteController.php b/app/Http/Controllers/QuoteController.php index 3a0062296098..a8ea0beaa476 100644 --- a/app/Http/Controllers/QuoteController.php +++ b/app/Http/Controllers/QuoteController.php @@ -26,6 +26,7 @@ use App\Ninja\Repositories\InvoiceRepository; use App\Ninja\Repositories\ClientRepository; use App\Events\QuoteInvitationWasApproved; use App\Services\InvoiceService; +use App\Http\Requests\InvoiceRequest; class QuoteController extends BaseController { @@ -78,10 +79,8 @@ class QuoteController extends BaseController return $this->invoiceService->getDatatable($accountId, $clientPublicId, ENTITY_QUOTE, $search); } - public function create($clientPublicId = 0) + public function create(InvoiceRequest $request, $clientPublicId = 0) { - $this->authorizeCreate(); - if (!Utils::hasFeature(FEATURE_QUOTES)) { return Redirect::to('/invoices/create'); } diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index e78462cd51ac..229a4751116e 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -75,11 +75,11 @@ class TaskController extends BaseController return $this->save(); } - public function show(TaskRequest $request) + public function show($publicId) { Session::reflash(); - return Redirect::to("tasks/{$request->task_id}/edit"); + return Redirect::to("tasks/{$publicId}/edit"); } /** diff --git a/app/Http/Requests/CreateDocumentRequest.php b/app/Http/Requests/CreateDocumentRequest.php new file mode 100644 index 000000000000..33330a90895e --- /dev/null +++ b/app/Http/Requests/CreateDocumentRequest.php @@ -0,0 +1,26 @@ +user()->can('create', ENTITY_DOCUMENT); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + + ]; + } +} diff --git a/app/Http/Requests/CreateInvoiceAPIRequest.php b/app/Http/Requests/CreateInvoiceAPIRequest.php new file mode 100644 index 000000000000..141d8788abc3 --- /dev/null +++ b/app/Http/Requests/CreateInvoiceAPIRequest.php @@ -0,0 +1,32 @@ +user()->can('create', ENTITY_INVOICE); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + $rules = [ + 'email' => 'required_without:client_id', + 'client_id' => 'required_without:email', + 'invoice_items' => 'valid_invoice_items', + 'invoice_number' => 'unique:invoices,invoice_number,,id,account_id,' . $this->user()->account_id, + 'discount' => 'positive', + ]; + + return $rules; + } +} diff --git a/app/Http/Requests/CreateInvoiceRequest.php b/app/Http/Requests/CreateInvoiceRequest.php index 8531a4074879..a3f556d408e9 100644 --- a/app/Http/Requests/CreateInvoiceRequest.php +++ b/app/Http/Requests/CreateInvoiceRequest.php @@ -1,11 +1,6 @@ user()->can('create', ENTITY_INVOICE); } /** @@ -25,13 +20,18 @@ class CreateInvoiceRequest extends Request public function rules() { $rules = [ - 'email' => 'required_without:client_id', - 'client_id' => 'required_without:email', + 'client.contacts' => 'valid_contacts', 'invoice_items' => 'valid_invoice_items', - 'invoice_number' => 'unique:invoices,invoice_number,,id,account_id,'.Auth::user()->account_id, + 'invoice_number' => 'required|unique:invoices,invoice_number,,id,account_id,' . $this->user()->account_id, 'discount' => 'positive', ]; + /* There's a problem parsing the dates + if (Request::get('is_recurring') && Request::get('start_date') && Request::get('end_date')) { + $rules['end_date'] = 'after' . Request::get('start_date'); + } + */ + return $rules; } } diff --git a/app/Http/Requests/DocumentRequest.php b/app/Http/Requests/DocumentRequest.php new file mode 100644 index 000000000000..a9d4396bc569 --- /dev/null +++ b/app/Http/Requests/DocumentRequest.php @@ -0,0 +1,7 @@ +entityType); - $this->entity = $class::scope($publicId)->withTrashed()->firstOrFail(); + + if (method_exists($class, 'withTrashed')) { + $this->entity = $class::scope($publicId)->withTrashed()->firstOrFail(); + } else { + $this->entity = $class::scope($publicId)->firstOrFail(); + } return $this->entity; } diff --git a/app/Http/Requests/InvoiceRequest.php b/app/Http/Requests/InvoiceRequest.php new file mode 100644 index 000000000000..8e3591d88de7 --- /dev/null +++ b/app/Http/Requests/InvoiceRequest.php @@ -0,0 +1,20 @@ +documents)) { + $expense->load('documents'); + } + + return $expense; + } + */ +} \ No newline at end of file diff --git a/app/Http/Requests/SaveInvoiceWithClientRequest.php b/app/Http/Requests/SaveInvoiceWithClientRequest.php deleted file mode 100644 index a75aee39c8c3..000000000000 --- a/app/Http/Requests/SaveInvoiceWithClientRequest.php +++ /dev/null @@ -1,45 +0,0 @@ - 'valid_contacts', - 'invoice_items' => 'valid_invoice_items', - 'invoice_number' => 'required|unique:invoices,invoice_number,'.$invoiceId.',id,account_id,'.Auth::user()->account_id, - 'discount' => 'positive', - ]; - - /* There's a problem parsing the dates - if (Request::get('is_recurring') && Request::get('start_date') && Request::get('end_date')) { - $rules['end_date'] = 'after' . Request::get('start_date'); - } - */ - - return $rules; - } -} diff --git a/app/Http/Requests/UpdateInvoiceAPIRequest.php b/app/Http/Requests/UpdateInvoiceAPIRequest.php new file mode 100644 index 000000000000..6fa3c4e92e45 --- /dev/null +++ b/app/Http/Requests/UpdateInvoiceAPIRequest.php @@ -0,0 +1,36 @@ +user()->can('edit', $this->entity()); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + if ($this->action == ACTION_ARCHIVE) { + return []; + } + + $invoiceId = $this->entity()->id; + + $rules = [ + 'invoice_items' => 'valid_invoice_items', + 'invoice_number' => 'unique:invoices,invoice_number,' . $invoiceId . ',id,account_id,' . $this->user()->account_id, + 'discount' => 'positive', + ]; + + return $rules; + } +} diff --git a/app/Http/Requests/UpdateInvoiceRequest.php b/app/Http/Requests/UpdateInvoiceRequest.php index 7827e2b1d1e1..96d112b157c2 100644 --- a/app/Http/Requests/UpdateInvoiceRequest.php +++ b/app/Http/Requests/UpdateInvoiceRequest.php @@ -1,11 +1,6 @@ user()->can('edit', $this->entity()); } /** @@ -24,19 +19,21 @@ class UpdateInvoiceRequest extends Request */ public function rules() { - if ($this->action == ACTION_ARCHIVE) { - return []; - } - - $publicId = $this->route('invoices'); - $invoiceId = Invoice::getPrivateId($publicId); - + $invoiceId = $this->entity()->id; + $rules = [ + 'client.contacts' => 'valid_contacts', 'invoice_items' => 'valid_invoice_items', - 'invoice_number' => 'unique:invoices,invoice_number,'.$invoiceId.',id,account_id,'.Auth::user()->account_id, + 'invoice_number' => 'required|unique:invoices,invoice_number,' . $invoiceId . ',id,account_id,' . $this->user()->account_id, 'discount' => 'positive', ]; + /* There's a problem parsing the dates + if (Request::get('is_recurring') && Request::get('start_date') && Request::get('end_date')) { + $rules['end_date'] = 'after' . Request::get('start_date'); + } + */ + return $rules; } } diff --git a/app/Http/routes.php b/app/Http/routes.php index 702cd1fcb98f..768c9c3d3d4c 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -48,8 +48,8 @@ Route::group(['middleware' => 'auth:client'], function() { Route::get('client/documents', 'PublicClientController@documentIndex'); Route::get('client/payments', 'PublicClientController@paymentIndex'); Route::get('client/dashboard', 'PublicClientController@dashboard'); - Route::get('client/document/js/{public_id}/{filename}', 'PublicClientController@getDocumentVFSJS'); - Route::get('client/document/{invitation_key}/{public_id}/{filename?}', 'PublicClientController@getDocument'); + Route::get('client/documents/js/{documents}/{filename}', 'PublicClientController@getDocumentVFSJS'); + Route::get('client/documents/{invitation_key}/{documents}/{filename?}', 'PublicClientController@getDocument'); Route::get('client/documents/{invitation_key}/{filename?}', 'PublicClientController@getInvoiceDocumentsZip'); Route::get('api/client.quotes', array('as'=>'api.client.quotes', 'uses'=>'PublicClientController@quoteDatatable')); @@ -134,20 +134,20 @@ Route::group(['middleware' => 'auth:user'], function() { Route::get('invoices/create/{client_id?}', 'InvoiceController@create'); Route::get('recurring_invoices/create/{client_id?}', 'InvoiceController@createRecurring'); Route::get('recurring_invoices', 'RecurringInvoiceController@index'); - Route::get('invoices/{public_id}/clone', 'InvoiceController@cloneInvoice'); + Route::get('invoices/{invoices}/clone', 'InvoiceController@cloneInvoice'); Route::post('invoices/bulk', 'InvoiceController@bulk'); Route::post('recurring_invoices/bulk', 'InvoiceController@bulk'); - Route::get('document/{public_id}/{filename?}', 'DocumentController@get'); - Route::get('document/js/{public_id}/{filename}', 'DocumentController@getVFSJS'); - Route::get('document/preview/{public_id}/{filename?}', 'DocumentController@getPreview'); + Route::get('documents/{documents}/{filename?}', 'DocumentController@get'); + Route::get('documents/js/{documents}/{filename}', 'DocumentController@getVFSJS'); + Route::get('documents/preview/{documents}/{filename?}', 'DocumentController@getPreview'); Route::post('document', 'DocumentController@postUpload'); Route::get('quotes/create/{client_id?}', 'QuoteController@create'); - Route::get('quotes/{public_id}/clone', 'InvoiceController@cloneInvoice'); - Route::get('quotes/{public_id}/edit', 'InvoiceController@edit'); - Route::put('quotes/{public_id}', 'InvoiceController@update'); - Route::get('quotes/{public_id}', 'InvoiceController@edit'); + Route::get('quotes/{invoices}/clone', 'InvoiceController@cloneInvoice'); + Route::get('quotes/{invoices}/edit', 'InvoiceController@edit'); + Route::put('quotes/{invoices}', 'InvoiceController@update'); + Route::get('quotes/{invoices}', 'InvoiceController@edit'); Route::post('quotes', 'InvoiceController@store'); Route::get('quotes', 'QuoteController@index'); Route::get('api/quotes/{client_id?}', array('as'=>'api.quotes', 'uses'=>'QuoteController@getDatatable')); diff --git a/app/Models/Document.php b/app/Models/Document.php index 0d7fc049aac8..6d9c24857143 100644 --- a/app/Models/Document.php +++ b/app/Models/Document.php @@ -173,11 +173,11 @@ class Document extends EntityModel } public function getUrl(){ - return url('document/'.$this->public_id.'/'.$this->name); + return url('documents/'.$this->public_id.'/'.$this->name); } public function getClientUrl($invitation){ - return url('client/document/'.$invitation->invitation_key.'/'.$this->public_id.'/'.$this->name); + return url('client/documents/'.$invitation->invitation_key.'/'.$this->public_id.'/'.$this->name); } public function isPDFEmbeddable(){ @@ -186,16 +186,16 @@ class Document extends EntityModel public function getVFSJSUrl(){ if(!$this->isPDFEmbeddable())return null; - return url('document/js/'.$this->public_id.'/'.$this->name.'.js'); + return url('documents/js/'.$this->public_id.'/'.$this->name.'.js'); } public function getClientVFSJSUrl(){ if(!$this->isPDFEmbeddable())return null; - return url('client/document/js/'.$this->public_id.'/'.$this->name.'.js'); + return url('client/documents/js/'.$this->public_id.'/'.$this->name.'.js'); } public function getPreviewUrl(){ - return $this->preview?url('document/preview/'.$this->public_id.'/'.$this->name.'.'.pathinfo($this->preview, PATHINFO_EXTENSION)):null; + return $this->preview?url('documents/preview/'.$this->public_id.'/'.$this->name.'.'.pathinfo($this->preview, PATHINFO_EXTENSION)):null; } public function toArray() diff --git a/app/Ninja/Repositories/DocumentRepository.php b/app/Ninja/Repositories/DocumentRepository.php index f33837051e97..094b4848ebe0 100644 --- a/app/Ninja/Repositories/DocumentRepository.php +++ b/app/Ninja/Repositories/DocumentRepository.php @@ -211,7 +211,7 @@ class DocumentRepository extends BaseRepository }) ->addColumn('name', function ($model) { return link_to( - '/client/document/'.$model->invitation_key.'/'.$model->public_id.'/'.$model->name, + '/client/documents/'.$model->invitation_key.'/'.$model->public_id.'/'.$model->name, $model->name, ['target'=>'_blank'] )->toHtml(); From 7f168f87b930ed07b5408cd2aa1e650d4ca37005 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 22:33:01 +0300 Subject: [PATCH 036/120] Working on form requests --- app/Http/Controllers/InvoiceController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 4dc60f8c7421..c57966138125 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -264,7 +264,7 @@ class InvoiceController extends BaseController return View::make('invoices.edit', $data); } - public function createRecurring(ClientRequest $request, $clientPublicId = 0) + public function createRecurring(InvoiceRequest $request, $clientPublicId = 0) { return self::create($request, $clientPublicId, true); } From e7296cf5137be361ef100e43a341a7bd67e3d0d9 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 22:36:12 +0300 Subject: [PATCH 037/120] Working on form requests --- app/Http/Controllers/InvoiceController.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index c57966138125..f25fb90da4cd 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -447,9 +447,9 @@ class InvoiceController extends BaseController Session::flash('message', $message); if ($action == 'clone') { - return $this->cloneInvoice($invoice->public_id); + return $this->cloneInvoice($request, $invoice->public_id); } elseif ($action == 'convert') { - return $this->convertQuote($invoice->public_id); + return $this->convertQuote($request, $invoice->public_id); } elseif ($action == 'email') { return $this->emailInvoice($invoice, Input::get('pdfupload')); } @@ -546,7 +546,7 @@ class InvoiceController extends BaseController } } - public function convertQuote($publicId) + public function convertQuote(InvoiceRequest $request, $publicId) { $invoice = Invoice::with('invoice_items')->scope($publicId)->firstOrFail(); $clone = $this->invoiceService->convertQuote($invoice); From 4db5a19885034dcec1096f9eb9540a609c47a548 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 1 May 2016 23:55:13 +0300 Subject: [PATCH 038/120] Refactored API index methods --- app/Http/Controllers/BaseAPIController.php | 34 +++- app/Http/Controllers/ClientApiController.php | 18 +- app/Http/Controllers/ExpenseApiController.php | 15 +- app/Http/Controllers/InvoiceApiController.php | 37 +--- app/Http/Controllers/InvoiceController.php | 8 +- app/Http/Controllers/PaymentApiController.php | 191 ++++++++---------- app/Http/Controllers/ProductApiController.php | 19 +- app/Http/Controllers/TaskApiController.php | 25 +-- app/Http/Controllers/TaxRateApiController.php | 16 +- app/Http/Controllers/UserApiController.php | 17 +- app/Http/Controllers/VendorApiController.php | 13 +- app/Http/Requests/InvoiceRequest.php | 11 +- app/Models/EntityModel.php | 10 + 13 files changed, 186 insertions(+), 228 deletions(-) diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index 3a3a1e0bece9..12b859e1f357 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -2,6 +2,8 @@ use Session; use Utils; +use Auth; +use Input; use Response; use Request; use League\Fractal; @@ -9,8 +11,10 @@ use League\Fractal\Manager; use League\Fractal\Resource\Item; use League\Fractal\Resource\Collection; use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use App\Models\EntityModel; use App\Ninja\Serializers\ArraySerializer; use League\Fractal\Serializer\JsonApiSerializer; +use Illuminate\Pagination\LengthAwarePaginator; /** * @SWG\Swagger( @@ -64,6 +68,23 @@ class BaseAPIController extends Controller } } + protected function returnList($query) + { + if ($clientPublicId = Input::get('client_id')) { + $filter = function($query) use ($clientPublicId) { + $query->where('public_id', '=', $clientPublicId); + }; + $query->whereHas('client', $filter); + } + + $transformerClass = EntityModel::getTransformerName($this->entityType); + $transformer = new $transformerClass(Auth::user()->account, Input::get('serializer')); + + $data = $this->createCollection($query, $transformer, $this->entityType); + + return $this->response($data); + } + protected function createItem($data, $transformer, $entityType) { if ($this->serializer && $this->serializer != API_SERIALIZER_JSON) { @@ -74,18 +95,19 @@ class BaseAPIController extends Controller return $this->manager->createData($resource)->toArray(); } - protected function createCollection($data, $transformer, $entityType, $paginator = false) + protected function createCollection($query, $transformer, $entityType) { if ($this->serializer && $this->serializer != API_SERIALIZER_JSON) { $entityType = null; } - $resource = new Collection($data, $transformer, $entityType); - - if ($paginator) { - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + if ($query instanceof LengthAwarePaginator) { + $resource = new Collection($query, $transformer, $entityType); + } else { + $resource = new Collection($query->get(), $transformer, $entityType); + $resource->setPaginator(new IlluminatePaginatorAdapter($query->paginate())); } - + return $this->manager->createData($resource)->toArray(); } diff --git a/app/Http/Controllers/ClientApiController.php b/app/Http/Controllers/ClientApiController.php index fd3b33df6aa5..51cd821f3f6b 100644 --- a/app/Http/Controllers/ClientApiController.php +++ b/app/Http/Controllers/ClientApiController.php @@ -18,6 +18,8 @@ class ClientApiController extends BaseAPIController protected $clientRepo; protected $clientService; + protected $entityType = ENTITY_CLIENT; + public function __construct(ClientRepository $clientRepo, ClientService $clientService) { parent::__construct(); @@ -53,26 +55,18 @@ class ClientApiController extends BaseAPIController { $clients = Client::scope() ->with($this->getIncluded()) - ->orderBy('created_at', 'desc')->withTrashed(); + ->orderBy('created_at', 'desc') + ->withTrashed(); // Filter by email if (Input::has('email')) { - $email = Input::get('email'); $clients = $clients->whereHas('contacts', function ($query) use ($email) { $query->where('email', $email); }); - } - - $clients = $clients->paginate(); - - $transformer = new ClientTransformer(Auth::user()->account, Input::get('serializer')); - $paginator = Client::scope()->withTrashed()->paginate(); - - $data = $this->createCollection($clients, $transformer, ENTITY_CLIENT, $paginator); - - return $this->response($data); + + return $this->returnList($clients); } /** diff --git a/app/Http/Controllers/ExpenseApiController.php b/app/Http/Controllers/ExpenseApiController.php index 88ff5497cefd..9b1a7e906244 100644 --- a/app/Http/Controllers/ExpenseApiController.php +++ b/app/Http/Controllers/ExpenseApiController.php @@ -1,5 +1,5 @@ withTrashed() ->orderBy('created_at','desc'); - $expenses = $expenses->paginate(); - - $transformer = new ExpenseTransformer(Auth::user()->account, Input::get('serializer')); - $paginator = Expense::scope()->withTrashed()->paginate(); - - $data = $this->createCollection($expenses, $transformer, ENTITY_EXPENSE, $paginator); - - return $this->response($data); - + return $this->returnList($expenses); } public function update() diff --git a/app/Http/Controllers/InvoiceApiController.php b/app/Http/Controllers/InvoiceApiController.php index fab8aa3bfd3f..ce5eaa1d72d6 100644 --- a/app/Http/Controllers/InvoiceApiController.php +++ b/app/Http/Controllers/InvoiceApiController.php @@ -26,6 +26,8 @@ class InvoiceApiController extends BaseAPIController { protected $invoiceRepo; + protected $entityType = ENTITY_INVOICE; + public function __construct(InvoiceService $invoiceService, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, PaymentRepository $paymentRepo, Mailer $mailer) { parent::__construct(); @@ -55,36 +57,12 @@ class InvoiceApiController extends BaseAPIController */ public function index() { - $paginator = Invoice::scope()->withTrashed(); - $invoices = Invoice::scope()->withTrashed() - ->with(array_merge(['invoice_items'], $this->getIncluded())); + $invoices = Invoice::scope() + ->withTrashed() + ->with(array_merge(['invoice_items'], $this->getIncluded())) + ->orderBy('created_at', 'desc'); - if ($clientPublicId = Input::get('client_id')) { - $filter = function($query) use ($clientPublicId) { - $query->where('public_id', '=', $clientPublicId); - }; - $invoices->whereHas('client', $filter); - $paginator->whereHas('client', $filter); - } - - $invoices = $invoices->orderBy('created_at', 'desc')->paginate(); - - /* - // Add the first invitation link to the data - foreach ($invoices as $key => $invoice) { - foreach ($invoice->invitations as $subKey => $invitation) { - $invoices[$key]['link'] = $invitation->getLink(); - } - unset($invoice['invitations']); - } - */ - - $transformer = new InvoiceTransformer(Auth::user()->account, Input::get('serializer')); - $paginator = $paginator->paginate(); - - $data = $this->createCollection($invoices, $transformer, 'invoices', $paginator); - - return $this->response($data); + return $this->returnList($invoices); } /** @@ -106,7 +84,6 @@ class InvoiceApiController extends BaseAPIController public function show($publicId) { - $invoice = Invoice::scope($publicId)->withTrashed()->first(); if(!$invoice) diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index f25fb90da4cd..1a652ae8962c 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -546,13 +546,13 @@ class InvoiceController extends BaseController } } - public function convertQuote(InvoiceRequest $request, $publicId) + public function convertQuote(InvoiceRequest $request) { - $invoice = Invoice::with('invoice_items')->scope($publicId)->firstOrFail(); - $clone = $this->invoiceService->convertQuote($invoice); + $clone = $this->invoiceService->convertQuote($request->entity()); Session::flash('message', trans('texts.converted_to_invoice')); - return Redirect::to('invoices/'.$clone->public_id); + + return Redirect::to('invoices/' . $clone->public_id); } public function cloneInvoice(InvoiceRequest $request, $publicId) diff --git a/app/Http/Controllers/PaymentApiController.php b/app/Http/Controllers/PaymentApiController.php index 7022f0c3e840..a7cab011fc36 100644 --- a/app/Http/Controllers/PaymentApiController.php +++ b/app/Http/Controllers/PaymentApiController.php @@ -17,13 +17,14 @@ class PaymentApiController extends BaseAPIController { protected $paymentRepo; + protected $entityType = ENTITY_PAYMENT; + public function __construct(PaymentRepository $paymentRepo, ContactMailer $contactMailer) { parent::__construct(); $this->paymentRepo = $paymentRepo; $this->contactMailer = $contactMailer; - } /** @@ -44,85 +45,71 @@ class PaymentApiController extends BaseAPIController */ public function index() { - $paginator = Payment::scope(); $payments = Payment::scope() - ->with('client.contacts', 'invitation', 'user', 'invoice')->withTrashed(); + ->withTrashed() + ->with(array_merge(['client.contacts', 'invitation', 'user', 'invoice'], $this->getIncluded())) + ->orderBy('created_at', 'desc'); - if ($clientPublicId = Input::get('client_id')) { - $filter = function($query) use ($clientPublicId) { - $query->where('public_id', '=', $clientPublicId); - }; - $payments->whereHas('client', $filter); - $paginator->whereHas('client', $filter); - } - - $payments = $payments->orderBy('created_at', 'desc')->paginate(); - $paginator = $paginator->paginate(); - - $transformer = new PaymentTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createCollection($payments, $transformer, 'payments', $paginator); - - return $this->response($data); + return $this->returnList($payments); } + /** + * @SWG\Put( + * path="/payments/{payment_id", + * summary="Update a payment", + * tags={"payment"}, + * @SWG\Parameter( + * in="body", + * name="body", + * @SWG\Schema(ref="#/definitions/Payment") + * ), + * @SWG\Response( + * response=200, + * description="Update payment", + * @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Payment")) + * ), + * @SWG\Response( + * response="default", + * description="an ""unexpected"" error" + * ) + * ) + */ - /** - * @SWG\Put( - * path="/payments/{payment_id", - * summary="Update a payment", - * tags={"payment"}, - * @SWG\Parameter( - * in="body", - * name="body", - * @SWG\Schema(ref="#/definitions/Payment") - * ), - * @SWG\Response( - * response=200, - * description="Update payment", - * @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Payment")) - * ), - * @SWG\Response( - * response="default", - * description="an ""unexpected"" error" - * ) - * ) - */ + public function update(Request $request, $publicId) + { + $data = Input::all(); + $data['public_id'] = $publicId; + $error = false; - public function update(Request $request, $publicId) - { - $data = Input::all(); - $data['public_id'] = $publicId; - $error = false; - - if ($request->action == ACTION_ARCHIVE) { - $payment = Payment::scope($publicId)->withTrashed()->firstOrFail(); - $this->paymentRepo->archive($payment); - - $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($payment, $transformer, 'invoice'); - - return $this->response($data); - } - - $payment = $this->paymentRepo->save($data); - - if ($error) { - return $error; - } - - /* - $invoice = Invoice::scope($data['invoice_id'])->with('client', 'invoice_items', 'invitations')->with(['payments' => function($query) { - $query->withTrashed(); - }])->withTrashed()->first(); - */ + if ($request->action == ACTION_ARCHIVE) { + $payment = Payment::scope($publicId)->withTrashed()->firstOrFail(); + $this->paymentRepo->archive($payment); $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); $data = $this->createItem($payment, $transformer, 'invoice'); return $this->response($data); - } + $payment = $this->paymentRepo->save($data); + + if ($error) { + return $error; + } + + /* + $invoice = Invoice::scope($data['invoice_id'])->with('client', 'invoice_items', 'invitations')->with(['payments' => function($query) { + $query->withTrashed(); + }])->withTrashed()->first(); + */ + + $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); + $data = $this->createItem($payment, $transformer, 'invoice'); + + return $this->response($data); + + } + /** * @SWG\Post( @@ -190,44 +177,44 @@ class PaymentApiController extends BaseAPIController } - /** - * @SWG\Delete( - * path="/payments/{payment_id}", - * summary="Delete a payment", - * tags={"payment"}, - * @SWG\Parameter( - * in="body", - * name="body", - * @SWG\Schema(ref="#/definitions/Payment") - * ), - * @SWG\Response( - * response=200, - * description="Delete payment", - * @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Payment")) - * ), - * @SWG\Response( - * response="default", - * description="an ""unexpected"" error" - * ) - * ) - */ + /** + * @SWG\Delete( + * path="/payments/{payment_id}", + * summary="Delete a payment", + * tags={"payment"}, + * @SWG\Parameter( + * in="body", + * name="body", + * @SWG\Schema(ref="#/definitions/Payment") + * ), + * @SWG\Response( + * response=200, + * description="Delete payment", + * @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Payment")) + * ), + * @SWG\Response( + * response="default", + * description="an ""unexpected"" error" + * ) + * ) + */ - public function destroy($publicId) - { + public function destroy($publicId) + { - $payment = Payment::scope($publicId)->withTrashed()->first(); - $invoiceId = $payment->invoice->public_id; + $payment = Payment::scope($publicId)->withTrashed()->first(); + $invoiceId = $payment->invoice->public_id; - $this->paymentRepo->delete($payment); + $this->paymentRepo->delete($payment); - /* - $invoice = Invoice::scope($invoiceId)->with('client', 'invoice_items', 'invitations')->with(['payments' => function($query) { - $query->withTrashed(); - }])->first(); - */ - $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($payment, $transformer, 'invoice'); + /* + $invoice = Invoice::scope($invoiceId)->with('client', 'invoice_items', 'invitations')->with(['payments' => function($query) { + $query->withTrashed(); + }])->first(); + */ + $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); + $data = $this->createItem($payment, $transformer, 'invoice'); - return $this->response($data); - } + return $this->response($data); + } } diff --git a/app/Http/Controllers/ProductApiController.php b/app/Http/Controllers/ProductApiController.php index 87bf89403b8a..26736f5ef5c2 100644 --- a/app/Http/Controllers/ProductApiController.php +++ b/app/Http/Controllers/ProductApiController.php @@ -20,8 +20,9 @@ use App\Services\ProductService; class ProductApiController extends BaseAPIController { protected $productService; - - protected $productRepo; + protected $productRepo; + + protected $entityType = ENTITY_PRODUCT; public function __construct(ProductService $productService, ProductRepository $productRepo) { @@ -33,17 +34,11 @@ class ProductApiController extends BaseAPIController public function index() { + $products = Product::scope() + ->withTrashed() + ->orderBy('created_at', 'desc'); - $products = Product::scope()->withTrashed(); - $products = $products->paginate(); - - $paginator = Product::scope()->withTrashed()->paginate(); - - $transformer = new ProductTransformer(\Auth::user()->account, $this->serializer); - $data = $this->createCollection($products, $transformer, 'products', $paginator); - - return $this->response($data); - + return $this->returnList($products); } public function getDatatable() diff --git a/app/Http/Controllers/TaskApiController.php b/app/Http/Controllers/TaskApiController.php index 926ed2f98ad3..cb39e0a1f369 100644 --- a/app/Http/Controllers/TaskApiController.php +++ b/app/Http/Controllers/TaskApiController.php @@ -13,6 +13,8 @@ class TaskApiController extends BaseAPIController { protected $taskRepo; + protected $entityType = ENTITY_TASK; + public function __construct(TaskRepository $taskRepo) { parent::__construct(); @@ -38,25 +40,12 @@ class TaskApiController extends BaseAPIController */ public function index() { - $paginator = Task::scope(); - $tasks = Task::scope() - ->with($this->getIncluded()); + $payments = Task::scope() + ->withTrashed() + ->with($this->getIncluded()) + ->orderBy('created_at', 'desc'); - if ($clientPublicId = Input::get('client_id')) { - $filter = function($query) use ($clientPublicId) { - $query->where('public_id', '=', $clientPublicId); - }; - $tasks->whereHas('client', $filter); - $paginator->whereHas('client', $filter); - } - - $tasks = $tasks->orderBy('created_at', 'desc')->paginate(); - $paginator = $paginator->paginate(); - $transformer = new TaskTransformer(\Auth::user()->account, Input::get('serializer')); - - $data = $this->createCollection($tasks, $transformer, 'tasks', $paginator); - - return $this->response($data); + return $this->returnList($payments); } /** diff --git a/app/Http/Controllers/TaxRateApiController.php b/app/Http/Controllers/TaxRateApiController.php index 7cacfcf8311b..ddc0ab4aa968 100644 --- a/app/Http/Controllers/TaxRateApiController.php +++ b/app/Http/Controllers/TaxRateApiController.php @@ -14,6 +14,8 @@ class TaxRateApiController extends BaseAPIController protected $taxRateService; protected $taxRateRepo; + protected $entityType = ENTITY_TAX_RATE; + public function __construct(TaxRateService $taxRateService, TaxRateRepository $taxRateRepo) { parent::__construct(); @@ -24,15 +26,11 @@ class TaxRateApiController extends BaseAPIController public function index() { - $taxRates = TaxRate::scope()->withTrashed(); - $taxRates = $taxRates->paginate(); - - $paginator = TaxRate::scope()->withTrashed()->paginate(); - - $transformer = new TaxRateTransformer(Auth::user()->account, $this->serializer); - $data = $this->createCollection($taxRates, $transformer, 'tax_rates', $paginator); - - return $this->response($data); + $taxRates = TaxRate::scope() + ->withTrashed() + ->orderBy('created_at', 'desc'); + + return $this->returnList($taxRates); } public function store(CreateTaxRateRequest $request) diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index fcd48787b134..8fda74b33cbd 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -14,6 +14,8 @@ class UserApiController extends BaseAPIController protected $userService; protected $userRepo; + protected $entityType = ENTITY_USER; + public function __construct(UserService $userService, UserRepository $userRepo) { parent::__construct(); @@ -24,16 +26,11 @@ class UserApiController extends BaseAPIController public function index() { - $user = Auth::user(); - $users = User::whereAccountId($user->account_id)->withTrashed(); - $users = $users->paginate(); - - $paginator = User::whereAccountId($user->account_id)->withTrashed()->paginate(); - - $transformer = new UserTransformer(Auth::user()->account, $this->serializer); - $data = $this->createCollection($users, $transformer, 'users', $paginator); - - return $this->response($data); + $users = User::whereAccountId(Auth::user()->account_id) + ->withTrashed() + ->orderBy('created_at', 'desc'); + + return $this->returnList($users); } /* diff --git a/app/Http/Controllers/VendorApiController.php b/app/Http/Controllers/VendorApiController.php index 4c32ee1eb3c5..b2487dfda2d3 100644 --- a/app/Http/Controllers/VendorApiController.php +++ b/app/Http/Controllers/VendorApiController.php @@ -14,6 +14,8 @@ class VendorApiController extends BaseAPIController { protected $vendorRepo; + protected $entityType = ENTITY_VENDOR; + public function __construct(VendorRepository $vendorRepo) { parent::__construct(); @@ -46,17 +48,12 @@ class VendorApiController extends BaseAPIController */ public function index() { - $vendors = Vendor::scope() + $vendors = Vendor::scope() ->with($this->getIncluded()) ->withTrashed() - ->orderBy('created_at', 'desc') - ->paginate(); + ->orderBy('created_at', 'desc'); - $transformer = new VendorTransformer(Auth::user()->account, Input::get('serializer')); - $paginator = Vendor::scope()->paginate(); - $data = $this->createCollection($vendors, $transformer, ENTITY_VENDOR, $paginator); - - return $this->response($data); + return $this->returnList($vendors); } /** diff --git a/app/Http/Requests/InvoiceRequest.php b/app/Http/Requests/InvoiceRequest.php index 8e3591d88de7..bf24c38839a9 100644 --- a/app/Http/Requests/InvoiceRequest.php +++ b/app/Http/Requests/InvoiceRequest.php @@ -4,17 +4,16 @@ class InvoiceRequest extends EntityRequest { protected $entityType = ENTITY_INVOICE; - /* public function entity() { - $expense = parent::entity(); + $invoice = parent::entity(); // eager load the contacts - if ($expense && ! count($expense->documents)) { - $expense->load('documents'); + if ($invoice && ! count($invoice->invoice_items)) { + $invoice->load('invoice_items'); } - return $expense; + return $invoice; } - */ + } \ No newline at end of file diff --git a/app/Models/EntityModel.php b/app/Models/EntityModel.php index 8d0da39d3fab..c2c9110fe306 100644 --- a/app/Models/EntityModel.php +++ b/app/Models/EntityModel.php @@ -101,6 +101,16 @@ class EntityModel extends Eloquent return $this->getName(); } + public static function getClassName($entityType) + { + return 'App\\Models\\' . ucwords(Utils::toCamelCase($entityType)); + } + + public static function getTransformerName($entityType) + { + return 'App\\Ninja\\Transformers\\' . ucwords(Utils::toCamelCase($entityType)) . 'Transformer'; + } + public function setNullValues() { foreach ($this->fillable as $field) { From 1b92f6648247235e359b0f3c4f57c23f55c03a99 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 09:33:48 +0300 Subject: [PATCH 039/120] Adding permissions to the API --- app/Http/Controllers/BaseAPIController.php | 16 +- app/Http/Controllers/ClientApiController.php | 8 +- .../_generated/AcceptanceTesterActions.php | 2 +- .../_generated/FunctionalTesterActions.php | 693 ++++++++++++++---- 4 files changed, 581 insertions(+), 138 deletions(-) diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index 12b859e1f357..4d03dea14b1d 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -70,6 +70,7 @@ class BaseAPIController extends Controller protected function returnList($query) { + //\DB::enableQueryLog(); if ($clientPublicId = Input::get('client_id')) { $filter = function($query) use ($clientPublicId) { $query->where('public_id', '=', $clientPublicId); @@ -77,11 +78,20 @@ class BaseAPIController extends Controller $query->whereHas('client', $filter); } + if ( ! Utils::hasPermission('view_all')){ + if ($this->entityType == ENTITY_USER) { + $query->where('id', '=', Auth::user()->id); + } else { + $query->where('user_id', '=', Auth::user()->id); + } + } + $transformerClass = EntityModel::getTransformerName($this->entityType); $transformer = new $transformerClass(Auth::user()->account, Input::get('serializer')); $data = $this->createCollection($query, $transformer, $this->entityType); + //return \DB::getQueryLog(); return $this->response($data); } @@ -101,11 +111,11 @@ class BaseAPIController extends Controller $entityType = null; } - if ($query instanceof LengthAwarePaginator) { - $resource = new Collection($query, $transformer, $entityType); - } else { + if (is_a($query, "Illuminate\Database\Eloquent\Builder")) { $resource = new Collection($query->get(), $transformer, $entityType); $resource->setPaginator(new IlluminatePaginatorAdapter($query->paginate())); + } else { + $resource = new Collection($query, $transformer, $entityType); } return $this->manager->createData($resource)->toArray(); diff --git a/app/Http/Controllers/ClientApiController.php b/app/Http/Controllers/ClientApiController.php index 51cd821f3f6b..4457973c7031 100644 --- a/app/Http/Controllers/ClientApiController.php +++ b/app/Http/Controllers/ClientApiController.php @@ -59,8 +59,7 @@ class ClientApiController extends BaseAPIController ->withTrashed(); // Filter by email - if (Input::has('email')) { - $email = Input::get('email'); + if ($email = Input::get('email')) { $clients = $clients->whereHas('contacts', function ($query) use ($email) { $query->where('email', $email); }); @@ -200,7 +199,6 @@ class ClientApiController extends BaseAPIController public function destroy($publicId) { - $client = Client::scope($publicId)->withTrashed()->first(); $this->clientRepo->delete($client); @@ -213,8 +211,6 @@ class ClientApiController extends BaseAPIController $data = $this->createItem($client, $transformer, ENTITY_CLIENT); return $this->response($data); - } - -} +} \ No newline at end of file diff --git a/tests/_support/_generated/AcceptanceTesterActions.php b/tests/_support/_generated/AcceptanceTesterActions.php index 82427d1e2a5e..d219111f887c 100644 --- a/tests/_support/_generated/AcceptanceTesterActions.php +++ b/tests/_support/_generated/AcceptanceTesterActions.php @@ -1,4 +1,4 @@ -amOnPage('/'); * // opens /register page * $I->amOnPage('/register'); - * ?> * ``` * * @param $page @@ -217,16 +216,31 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Checks that the current page contains the given string. - * Specify a locator as the second parameter to match a specific region. + * Checks that the current page contains the given string (case insensitive). + * + * You can specify a specific HTML element (via CSS or XPath) as the second + * parameter to only search within that element. * * ``` php * see('Logout'); // I can suppose user is logged in - * $I->see('Sign Up','h1'); // I can suppose it's a signup page - * $I->see('Sign Up','//body/h1'); // with XPath - * ?> + * $I->see('Logout'); // I can suppose user is logged in + * $I->see('Sign Up', 'h1'); // I can suppose it's a signup page + * $I->see('Sign Up', '//body/h1'); // with XPath * ``` + * + * Note that the search is done after stripping all HTML tags from the body, + * so `$I->see('strong')` will return true for strings like: + * + * - `

    I am Stronger than thou

    ` + * - `` + * + * But will *not* be true for strings like: + * + * - `Home` + * - `
    Home` + * - `` + * + * For checking the raw source code, use `seeInSource()`. * * @param $text * @param null $selector @@ -239,16 +253,31 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Checks that the current page contains the given string. - * Specify a locator as the second parameter to match a specific region. + * Checks that the current page contains the given string (case insensitive). + * + * You can specify a specific HTML element (via CSS or XPath) as the second + * parameter to only search within that element. * * ``` php * see('Logout'); // I can suppose user is logged in - * $I->see('Sign Up','h1'); // I can suppose it's a signup page - * $I->see('Sign Up','//body/h1'); // with XPath - * ?> + * $I->see('Logout'); // I can suppose user is logged in + * $I->see('Sign Up', 'h1'); // I can suppose it's a signup page + * $I->see('Sign Up', '//body/h1'); // with XPath * ``` + * + * Note that the search is done after stripping all HTML tags from the body, + * so `$I->see('strong')` will return true for strings like: + * + * - `

    I am Stronger than thou

    ` + * - `` + * + * But will *not* be true for strings like: + * + * - `Home` + * - `
    Home` + * - `` + * + * For checking the raw source code, use `seeInSource()`. * * @param $text * @param null $selector @@ -262,16 +291,29 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Checks that the current page doesn't contain the text specified. + * Checks that the current page doesn't contain the text specified (case insensitive). * Give a locator as the second parameter to match a specific region. * * ```php * dontSee('Login'); // I can suppose user is already logged in - * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page - * $I->dontSee('Sign Up','//body/h1'); // with XPath - * ?> + * $I->dontSee('Login'); // I can suppose user is already logged in + * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page + * $I->dontSee('Sign Up','//body/h1'); // with XPath * ``` + * + * Note that the search is done after stripping all HTML tags from the body, + * so `$I->dontSee('strong')` will fail on strings like: + * + * - `

    I am Stronger than thou

    ` + * - `` + * + * But will ignore strings like: + * + * - `Home` + * - `
    Home` + * - `` + * + * For checking the raw source code, use `seeInSource()`. * * @param $text * @param null $selector @@ -284,16 +326,29 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Checks that the current page doesn't contain the text specified. + * Checks that the current page doesn't contain the text specified (case insensitive). * Give a locator as the second parameter to match a specific region. * * ```php * dontSee('Login'); // I can suppose user is already logged in - * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page - * $I->dontSee('Sign Up','//body/h1'); // with XPath - * ?> + * $I->dontSee('Login'); // I can suppose user is already logged in + * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page + * $I->dontSee('Sign Up','//body/h1'); // with XPath * ``` + * + * Note that the search is done after stripping all HTML tags from the body, + * so `$I->dontSee('strong')` will fail on strings like: + * + * - `

    I am Stronger than thou

    ` + * - `` + * + * But will ignore strings like: + * + * - `Home` + * - `
    Home` + * - `` + * + * For checking the raw source code, use `seeInSource()`. * * @param $text * @param null $selector @@ -304,6 +359,80 @@ trait FunctionalTesterActions } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that the current page contains the given string in its + * raw source code. + * + * ``` php + * seeInSource('

    Green eggs & ham

    '); + * ``` + * + * @param $raw + * Conditional Assertion: Test won't be stopped on fail + * @see \Codeception\Lib\InnerBrowser::seeInSource() + */ + public function canSeeInSource($raw) { + return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInSource', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that the current page contains the given string in its + * raw source code. + * + * ``` php + * seeInSource('

    Green eggs & ham

    '); + * ``` + * + * @param $raw + * @see \Codeception\Lib\InnerBrowser::seeInSource() + */ + public function seeInSource($raw) { + return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInSource', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that the current page contains the given string in its + * raw source code. + * + * ```php + * dontSeeInSource('

    Green eggs & ham

    '); + * ``` + * + * @param $raw + * Conditional Assertion: Test won't be stopped on fail + * @see \Codeception\Lib\InnerBrowser::dontSeeInSource() + */ + public function cantSeeInSource($raw) { + return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInSource', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that the current page contains the given string in its + * raw source code. + * + * ```php + * dontSeeInSource('

    Green eggs & ham

    '); + * ``` + * + * @param $raw + * @see \Codeception\Lib\InnerBrowser::dontSeeInSource() + */ + public function dontSeeInSource($raw) { + return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInSource', func_get_args())); + } + + /** * [!] Method is generated. Documentation taken from corresponding module. * @@ -645,7 +774,6 @@ trait FunctionalTesterActions * * @param null $uri * - * @internal param $url * @return mixed * @see \Codeception\Lib\InnerBrowser::grabFromCurrentUrl() */ @@ -1074,15 +1202,28 @@ trait FunctionalTesterActions * [!] Method is generated. Documentation taken from corresponding module. * * Submits the given form on the page, optionally with the given form - * values. Give the form fields values as an array. + * values. Pass the form field's values as an array in the second + * parameter. * - * Skipped fields will be filled by their values from the page. + * Although this function can be used as a short-hand version of + * `fillField()`, `selectOption()`, `click()` etc. it has some important + * differences: + * + * * Only field *names* may be used, not CSS/XPath selectors nor field labels + * * If a field is sent to this function that does *not* exist on the page, + * it will silently be added to the HTTP request. This is helpful for testing + * some types of forms, but be aware that you will *not* get an exception + * like you would if you called `fillField()` or `selectOption()` with + * a missing field. + * + * Fields that are not provided will be filled by their values from the page, + * or from any previous calls to `fillField()`, `selectOption()` etc. * You don't need to click the 'Submit' button afterwards. * This command itself triggers the request to form's action. * - * You can optionally specify what button's value to include - * in the request with the last parameter as an alternative to - * explicitly setting its value in the second parameter, as + * You can optionally specify which button's value to include + * in the request with the last parameter (as an alternative to + * explicitly setting its value in the second parameter), as * button values are not otherwise included in the request. * * Examples: @@ -1156,7 +1297,8 @@ trait FunctionalTesterActions * ); * ``` * - * Pair this with seeInFormFields for quick testing magic. + * This function works well when paired with `seeInFormFields()` + * for quickly testing CRUD interfaces and form validation logic. * * ``` php * true, * // ... * ]; - * $I->submitForm('//form[@id=my-form]', $form, 'submitButton'); + * $I->submitForm('#my-form', $form, 'submitButton'); * // $I->amOnPage('/path/to/form-page') may be needed - * $I->seeInFormFields('//form[@id=my-form]', $form); - * ?> + * $I->seeInFormFields('#my-form', $form); * ``` * * Parameter values can be set to arrays for multiple input fields * of the same name, or multi-select combo boxes. For checkboxes, - * either the string value can be used, or boolean values which will + * you can use either the string value or boolean `true`/`false` which will * be replaced by the checkbox's value in the DOM. * * ``` php @@ -1183,7 +1324,7 @@ trait FunctionalTesterActions * 'field1' => 'value', * 'checkbox' => [ * 'value of first checkbox', - * 'value of second checkbox, + * 'value of second checkbox', * ], * 'otherCheckboxes' => [ * true, @@ -1195,27 +1336,29 @@ trait FunctionalTesterActions * 'second option value' * ] * ]); - * ?> * ``` * * Mixing string and boolean values for a checkbox's value is not supported * and may produce unexpected results. * - * Field names ending in "[]" must be passed without the trailing square + * Field names ending in `[]` must be passed without the trailing square * bracket characters, and must contain an array for its value. This allows * submitting multiple values with the same name, consider: * * ```php + * submitForm('#my-form', [ * 'field[]' => 'value', - * 'field[]' => 'another value', // 'field[]' is already a defined key + * 'field[]' => 'another value', // 'field[]' is already a defined key * ]); * ``` * * The solution is to pass an array value: * * ```php - * // this way both values are submitted + * submitForm('#my-form', [ * 'field' => [ * 'value', @@ -1454,7 +1597,7 @@ trait FunctionalTesterActions * * @param $cssOrXpath * @param $attribute - * @internal param $element + * * @return mixed * @see \Codeception\Lib\InnerBrowser::grabAttributeFrom() */ @@ -1466,7 +1609,28 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * + * Grabs either the text content, or attribute values, of nodes + * matched by $cssOrXpath and returns them as an array. + * + * ```html + * First + * Second + * Third + * ``` + * + * ```php + * grabMultiple('a'); + * + * // would return ['#first', '#second', '#third'] + * $aLinks = $I->grabMultiple('a', 'href'); + * ?> + * ``` + * + * @param $cssOrXpath + * @param $attribute + * @return string[] * @see \Codeception\Lib\InnerBrowser::grabMultiple() */ public function grabMultiple($cssOrXpath, $attribute = null) { @@ -1491,7 +1655,7 @@ trait FunctionalTesterActions * [!] Method is generated. Documentation taken from corresponding module. * * Sets a cookie with the given name and value. - * You can set additional cookie params like `domain`, `path`, `expire`, `secure` in array passed as last argument. + * You can set additional cookie params like `domain`, `path`, `expires`, `secure` in array passed as last argument. * * ``` php * + * ``` + * + * ``` php + * switchToIframe("another_frame"); + * ``` + * + * @param string $name + * @see \Codeception\Lib\InnerBrowser::switchToIframe() + */ + public function switchToIframe($name) { + return $this->getScenario()->runStep(new \Codeception\Step\Action('switchToIframe', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Moves back in history. + * + * @param int $numberOfSteps (default value 1) + * @see \Codeception\Lib\InnerBrowser::moveBack() + */ + public function moveBack($numberOfSteps = null) { + return $this->getScenario()->runStep(new \Codeception\Step\Action('moveBack', func_get_args())); + } + + /** * [!] Method is generated. Documentation taken from corresponding module. * @@ -1996,6 +2197,40 @@ trait FunctionalTesterActions } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Enable Laravel exception handling. + * + * ``` php + * enableExceptionHandling(); + * ?> + * ``` + * @see \Codeception\Module\Laravel5::enableExceptionHandling() + */ + public function enableExceptionHandling() { + return $this->getScenario()->runStep(new \Codeception\Step\Action('enableExceptionHandling', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Disable Laravel exception handling. + * + * ``` php + * disableExceptionHandling(); + * ?> + * ``` + * @see \Codeception\Module\Laravel5::disableExceptionHandling() + */ + public function disableExceptionHandling() { + return $this->getScenario()->runStep(new \Codeception\Step\Action('disableExceptionHandling', func_get_args())); + } + + /** * [!] Method is generated. Documentation taken from corresponding module. * @@ -2016,17 +2251,99 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Enable middleware for the next requests. + * Disable events for the next requests. * * ``` php * enableMiddleware(); + * $I->disableEvents(); * ?> * ``` - * @see \Codeception\Module\Laravel5::enableMiddleware() + * @see \Codeception\Module\Laravel5::disableEvents() */ - public function enableMiddleware() { - return $this->getScenario()->runStep(new \Codeception\Step\Action('enableMiddleware', func_get_args())); + public function disableEvents() { + return $this->getScenario()->runStep(new \Codeception\Step\Action('disableEvents', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Make sure events fired during the test. + * + * ``` php + * seeEventTriggered('App\MyEvent'); + * $I->seeEventTriggered(new App\Events\MyEvent()); + * $I->seeEventTriggered('App\MyEvent', 'App\MyOtherEvent'); + * $I->seeEventTriggered(['App\MyEvent', 'App\MyOtherEvent']); + * ?> + * ``` + * @param $events + * Conditional Assertion: Test won't be stopped on fail + * @see \Codeception\Module\Laravel5::seeEventTriggered() + */ + public function canSeeEventTriggered($events) { + return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeEventTriggered', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Make sure events fired during the test. + * + * ``` php + * seeEventTriggered('App\MyEvent'); + * $I->seeEventTriggered(new App\Events\MyEvent()); + * $I->seeEventTriggered('App\MyEvent', 'App\MyOtherEvent'); + * $I->seeEventTriggered(['App\MyEvent', 'App\MyOtherEvent']); + * ?> + * ``` + * @param $events + * @see \Codeception\Module\Laravel5::seeEventTriggered() + */ + public function seeEventTriggered($events) { + return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeEventTriggered', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Make sure events did not fire during the test. + * + * ``` php + * dontSeeEventTriggered('App\MyEvent'); + * $I->dontSeeEventTriggered(new App\Events\MyEvent()); + * $I->dontSeeEventTriggered('App\MyEvent', 'App\MyOtherEvent'); + * $I->dontSeeEventTriggered(['App\MyEvent', 'App\MyOtherEvent']); + * ?> + * ``` + * @param $events + * Conditional Assertion: Test won't be stopped on fail + * @see \Codeception\Module\Laravel5::dontSeeEventTriggered() + */ + public function cantSeeEventTriggered($events) { + return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeEventTriggered', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Make sure events did not fire during the test. + * + * ``` php + * dontSeeEventTriggered('App\MyEvent'); + * $I->dontSeeEventTriggered(new App\Events\MyEvent()); + * $I->dontSeeEventTriggered('App\MyEvent', 'App\MyOtherEvent'); + * $I->dontSeeEventTriggered(['App\MyEvent', 'App\MyOtherEvent']); + * ?> + * ``` + * @param $events + * @see \Codeception\Module\Laravel5::dontSeeEventTriggered() + */ + public function dontSeeEventTriggered($events) { + return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeEventTriggered', func_get_args())); } @@ -2050,6 +2367,41 @@ trait FunctionalTesterActions } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that current url matches route + * + * ``` php + * seeCurrentRouteIs('posts.index'); + * ?> + * ``` + * @param $routeName + * Conditional Assertion: Test won't be stopped on fail + * @see \Codeception\Module\Laravel5::seeCurrentRouteIs() + */ + public function canSeeCurrentRouteIs($routeName) { + return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentRouteIs', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Checks that current url matches route + * + * ``` php + * seeCurrentRouteIs('posts.index'); + * ?> + * ``` + * @param $routeName + * @see \Codeception\Module\Laravel5::seeCurrentRouteIs() + */ + public function seeCurrentRouteIs($routeName) { + return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCurrentRouteIs', func_get_args())); + } + + /** * [!] Method is generated. Documentation taken from corresponding module. * @@ -2070,43 +2422,6 @@ trait FunctionalTesterActions } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current url matches route - * - * ``` php - * seeCurrentRouteIs('posts.index'); - * ?> - * ``` - * @param $route - * @param array $params - * Conditional Assertion: Test won't be stopped on fail - * @see \Codeception\Module\Laravel5::seeCurrentRouteIs() - */ - public function canSeeCurrentRouteIs($route, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentRouteIs', func_get_args())); - } - /** - * [!] Method is generated. Documentation taken from corresponding module. - * - * Checks that current url matches route - * - * ``` php - * seeCurrentRouteIs('posts.index'); - * ?> - * ``` - * @param $route - * @param array $params - * @see \Codeception\Module\Laravel5::seeCurrentRouteIs() - */ - public function seeCurrentRouteIs($route, $params = null) { - return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCurrentRouteIs', func_get_args())); - } - - /** * [!] Method is generated. Documentation taken from corresponding module. * @@ -2119,11 +2434,10 @@ trait FunctionalTesterActions * ``` * * @param $action - * @param array $params * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Laravel5::seeCurrentActionIs() */ - public function canSeeCurrentActionIs($action, $params = null) { + public function canSeeCurrentActionIs($action) { return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentActionIs', func_get_args())); } /** @@ -2138,10 +2452,9 @@ trait FunctionalTesterActions * ``` * * @param $action - * @param array $params * @see \Codeception\Module\Laravel5::seeCurrentActionIs() */ - public function seeCurrentActionIs($action, $params = null) { + public function seeCurrentActionIs($action) { return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCurrentActionIs', func_get_args())); } @@ -2159,7 +2472,7 @@ trait FunctionalTesterActions * ``` * * @param string|array $key - * @param mixed $value + * @param mixed|null $value * @return void * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Laravel5::seeInSession() @@ -2180,7 +2493,7 @@ trait FunctionalTesterActions * ``` * * @param string|array $key - * @param mixed $value + * @param mixed|null $value * @return void * @see \Codeception\Module\Laravel5::seeInSession() */ @@ -2270,16 +2583,53 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Assert that specific form error messages are set in the view. - * - * Useful for validation messages e.g. - * return `Redirect::to('register')->withErrors($validator);` - * - * Example of Usage + * Assert that there are no form errors bound to the View. * * ``` php * seeFormErrorMessages(array('username'=>'Invalid Username')); + * $I->dontSeeFormErrors(); + * ?> + * ``` + * + * @return bool + * Conditional Assertion: Test won't be stopped on fail + * @see \Codeception\Module\Laravel5::dontSeeFormErrors() + */ + public function cantSeeFormErrors() { + return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeFormErrors', func_get_args())); + } + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Assert that there are no form errors bound to the View. + * + * ``` php + * dontSeeFormErrors(); + * ?> + * ``` + * + * @return bool + * @see \Codeception\Module\Laravel5::dontSeeFormErrors() + */ + public function dontSeeFormErrors() { + return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeFormErrors', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Assert that specific form error messages are set in the view. + * + * This method calls `seeFormErrorMessage` for each entry in the `$bindings` array. + * + * ``` php + * seeFormErrorMessages([ + * 'username' => 'Invalid Username', + * 'password' => null, + * ]); * ?> * ``` * @param array $bindings @@ -2294,14 +2644,14 @@ trait FunctionalTesterActions * * Assert that specific form error messages are set in the view. * - * Useful for validation messages e.g. - * return `Redirect::to('register')->withErrors($validator);` - * - * Example of Usage + * This method calls `seeFormErrorMessage` for each entry in the `$bindings` array. * * ``` php * seeFormErrorMessages(array('username'=>'Invalid Username')); + * $I->seeFormErrorMessages([ + * 'username' => 'Invalid Username', + * 'password' => null, + * ]); * ?> * ``` * @param array $bindings @@ -2315,48 +2665,50 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Assert that specific form error message is set in the view. + * Assert that a specific form error message is set in the view. * - * Useful for validation messages and generally messages array - * e.g. - * return `Redirect::to('register')->withErrors($validator);` + * If you want to assert that there is a form error message for a specific key + * but don't care about the actual error message you can omit `$expectedErrorMessage`. * - * Example of Usage + * If you do pass `$expectedErrorMessage`, this method checks if the actual error message for a key + * contains `$expectedErrorMessage`. * * ``` php * seeFormErrorMessage('username'); * $I->seeFormErrorMessage('username', 'Invalid Username'); * ?> * ``` * @param string $key - * @param string $errorMessage + * @param string|null $expectedErrorMessage * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Laravel5::seeFormErrorMessage() */ - public function canSeeFormErrorMessage($key, $errorMessage) { + public function canSeeFormErrorMessage($key, $expectedErrorMessage = null) { return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeFormErrorMessage', func_get_args())); } /** * [!] Method is generated. Documentation taken from corresponding module. * - * Assert that specific form error message is set in the view. + * Assert that a specific form error message is set in the view. * - * Useful for validation messages and generally messages array - * e.g. - * return `Redirect::to('register')->withErrors($validator);` + * If you want to assert that there is a form error message for a specific key + * but don't care about the actual error message you can omit `$expectedErrorMessage`. * - * Example of Usage + * If you do pass `$expectedErrorMessage`, this method checks if the actual error message for a key + * contains `$expectedErrorMessage`. * * ``` php * seeFormErrorMessage('username'); * $I->seeFormErrorMessage('username', 'Invalid Username'); * ?> * ``` * @param string $key - * @param string $errorMessage + * @param string|null $expectedErrorMessage * @see \Codeception\Module\Laravel5::seeFormErrorMessage() */ - public function seeFormErrorMessage($key, $errorMessage) { + public function seeFormErrorMessage($key, $expectedErrorMessage = null) { return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeFormErrorMessage', func_get_args())); } @@ -2368,8 +2720,19 @@ trait FunctionalTesterActions * Takes either an object that implements the User interface or * an array of credentials. * + * ``` php + * amLoggedAs(['username' => 'jane@example.com', 'password' => 'password']); + * + * // provide User object + * $I->amLoggedAs( new User ); + * + * // can be verified with $I->seeAuthentication(); + * ?> + * ``` * @param \Illuminate\Contracts\Auth\User|array $user - * @param string $driver + * @param string|null $driver The authentication driver for Laravel <= 5.1.*, guard name for Laravel >= 5.2 * @return void * @see \Codeception\Module\Laravel5::amLoggedAs() */ @@ -2381,7 +2744,7 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Logs user out + * Logout user. * @see \Codeception\Module\Laravel5::logout() */ public function logout() { @@ -2392,20 +2755,24 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Checks that user is authenticated + * Checks that a user is authenticated. + * You can specify the guard that should be use for Laravel >= 5.2. + * @param string|null $guard * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Laravel5::seeAuthentication() */ - public function canSeeAuthentication() { + public function canSeeAuthentication($guard = null) { return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeAuthentication', func_get_args())); } /** * [!] Method is generated. Documentation taken from corresponding module. * - * Checks that user is authenticated + * Checks that a user is authenticated. + * You can specify the guard that should be use for Laravel >= 5.2. + * @param string|null $guard * @see \Codeception\Module\Laravel5::seeAuthentication() */ - public function seeAuthentication() { + public function seeAuthentication($guard = null) { return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeAuthentication', func_get_args())); } @@ -2413,20 +2780,24 @@ trait FunctionalTesterActions /** * [!] Method is generated. Documentation taken from corresponding module. * - * Check that user is not authenticated + * Check that user is not authenticated. + * You can specify the guard that should be use for Laravel >= 5.2. + * @param string|null $guard * Conditional Assertion: Test won't be stopped on fail * @see \Codeception\Module\Laravel5::dontSeeAuthentication() */ - public function cantSeeAuthentication() { + public function cantSeeAuthentication($guard = null) { return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeAuthentication', func_get_args())); } /** * [!] Method is generated. Documentation taken from corresponding module. * - * Check that user is not authenticated + * Check that user is not authenticated. + * You can specify the guard that should be use for Laravel >= 5.2. + * @param string|null $guard * @see \Codeception\Module\Laravel5::dontSeeAuthentication() */ - public function dontSeeAuthentication() { + public function dontSeeAuthentication($guard = null) { return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeAuthentication', func_get_args())); } @@ -2437,7 +2808,6 @@ trait FunctionalTesterActions * Return an instance of a class from the IoC Container. * (http://laravel.com/docs/ioc) * - * Example * ``` php * getScenario()->runStep(new \Codeception\Step\Action('grabRecord', func_get_args())); } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * + * @see \Codeception\Module\Laravel5::haveModel() + */ + public function haveModel($model, $attributes = null, $name = null, $times = null) { + return $this->getScenario()->runStep(new \Codeception\Step\Action('haveModel', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Use Laravel's model factory to create a model. + * Can only be used with Laravel 5.1 and later. + * + * ``` php + * createModel('App\User'); + * $I->createModel('App\User', ['name' => 'John Doe']); + * $I->createModel('App\User', [], 'admin'); + * $I->createModel('App\User', [], 'admin', 3); + * ?> + * ``` + * + * @see http://laravel.com/docs/5.1/testing#model-factories + * @param string $model + * @param array $attributes + * @param string $name + * @param int $times + * @return mixed + * @see \Codeception\Module\Laravel5::createModel() + */ + public function createModel($model, $attributes = null, $name = null, $times = null) { + return $this->getScenario()->runStep(new \Codeception\Step\Action('createModel', func_get_args())); + } + + + /** + * [!] Method is generated. Documentation taken from corresponding module. + * + * Use Laravel's model factory to make a model. + * Can only be used with Laravel 5.1 and later. + * + * ``` php + * makeModel('App\User'); + * $I->makeModel('App\User', ['name' => 'John Doe']); + * $I->makeModel('App\User', [], 'admin'); + * $I->makeModel('App\User', [], 'admin', 3); + * ?> + * ``` + * + * @see http://laravel.com/docs/5.1/testing#model-factories + * @param string $model + * @param array $attributes + * @param string $name + * @param int $times + * @return mixed + * @see \Codeception\Module\Laravel5::makeModel() + */ + public function makeModel($model, $attributes = null, $name = null, $times = null) { + return $this->getScenario()->runStep(new \Codeception\Step\Action('makeModel', func_get_args())); + } } From 1d6011caadf30710e4a4d5f57485b69a2faff94d Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 11:38:01 +0300 Subject: [PATCH 040/120] Working on permissions in the API --- app/Http/Controllers/AccountApiController.php | 7 ++ app/Http/Controllers/BaseAPIController.php | 25 +++++- app/Http/Controllers/ClientApiController.php | 69 ++-------------- app/Http/Controllers/ExpenseApiController.php | 2 +- app/Http/Controllers/InvoiceApiController.php | 2 +- app/Http/Controllers/PaymentApiController.php | 78 +++---------------- app/Http/Controllers/ProductApiController.php | 2 +- app/Http/Controllers/TaskApiController.php | 2 +- app/Http/Controllers/TaxRateApiController.php | 2 +- app/Http/Controllers/UserApiController.php | 2 +- app/Http/Controllers/VendorApiController.php | 2 +- app/Http/Requests/CreatePaymentAPIRequest.php | 48 ++++++++++++ app/Http/routes.php | 2 +- app/Models/EntityModel.php | 2 +- 14 files changed, 107 insertions(+), 138 deletions(-) create mode 100644 app/Http/Requests/CreatePaymentAPIRequest.php diff --git a/app/Http/Controllers/AccountApiController.php b/app/Http/Controllers/AccountApiController.php index 02697ba039de..65175cdd5ba1 100644 --- a/app/Http/Controllers/AccountApiController.php +++ b/app/Http/Controllers/AccountApiController.php @@ -34,6 +34,13 @@ class AccountApiController extends BaseAPIController $this->accountRepo = $accountRepo; } + public function ping() + { + $headers = Utils::getApiHeaders(); + + return Response::make(RESULT_SUCCESS, 200, $headers); + } + public function register(RegisterRequest $request) { diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index 4d03dea14b1d..7bfdfc931c90 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -68,7 +68,19 @@ class BaseAPIController extends Controller } } - protected function returnList($query) + protected function handleAction($request) + { + $entity = $request->entity(); + $action = $request->action; + + $repo = Utils::toCamelCase($this->entityType) . 'Repo'; + + $this->$repo->$action($entity); + + return $this->itemResponse($entity); + } + + protected function listResponse($query) { //\DB::enableQueryLog(); if ($clientPublicId = Input::get('client_id')) { @@ -95,6 +107,16 @@ class BaseAPIController extends Controller return $this->response($data); } + protected function itemResponse($item) + { + $transformerClass = EntityModel::getTransformerName($this->entityType); + $transformer = new $transformerClass(Auth::user()->account, Input::get('serializer')); + + $data = $this->createItem($item, $transformer, $this->entityType); + + return $this->response($data); + } + protected function createItem($data, $transformer, $entityType) { if ($this->serializer && $this->serializer != API_SERIALIZER_JSON) { @@ -155,7 +177,6 @@ class BaseAPIController extends Controller } - protected function getIncluded() { $data = ['user']; diff --git a/app/Http/Controllers/ClientApiController.php b/app/Http/Controllers/ClientApiController.php index 4457973c7031..83689e274318 100644 --- a/app/Http/Controllers/ClientApiController.php +++ b/app/Http/Controllers/ClientApiController.php @@ -10,29 +10,19 @@ use App\Ninja\Repositories\ClientRepository; use App\Http\Requests\CreateClientRequest; use App\Http\Controllers\BaseAPIController; use App\Ninja\Transformers\ClientTransformer; -use App\Services\ClientService; use App\Http\Requests\UpdateClientRequest; class ClientApiController extends BaseAPIController { protected $clientRepo; - protected $clientService; protected $entityType = ENTITY_CLIENT; - public function __construct(ClientRepository $clientRepo, ClientService $clientService) + public function __construct(ClientRepository $clientRepo) { parent::__construct(); $this->clientRepo = $clientRepo; - $this->clientService = $clientService; - } - - public function ping() - { - $headers = Utils::getApiHeaders(); - - return Response::make('', 200, $headers); } /** @@ -65,7 +55,7 @@ class ClientApiController extends BaseAPIController }); } - return $this->returnList($clients); + return $this->listResponse($clients); } /** @@ -93,14 +83,7 @@ class ClientApiController extends BaseAPIController { $client = $this->clientRepo->save($request->input()); - $client = Client::scope($client->public_id) - ->with('country', 'contacts', 'industry', 'size', 'currency') - ->first(); - - $transformer = new ClientTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($client, $transformer, ENTITY_CLIENT); - - return $this->response($data); + return $this->itemResponse($client); } /** @@ -127,51 +110,15 @@ class ClientApiController extends BaseAPIController public function update(UpdateClientRequest $request, $publicId) { - if ($request->action == ACTION_ARCHIVE) { - - - $client = Client::scope($publicId)->withTrashed()->first(); - - if(!$client) - return $this->errorResponse(['message'=>'Record not found'], 400); - - $this->clientRepo->archive($client); - - $transformer = new ClientTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($client, $transformer, ENTITY_CLIENT); - - return $this->response($data); + if ($request->action) { + return $this->handleAction($request); } - else if ($request->action == ACTION_RESTORE){ - - $client = Client::scope($publicId)->withTrashed()->first(); - - if(!$client) - return $this->errorResponse(['message'=>'Client not found.'], 400); - - $this->clientRepo->restore($client); - - $transformer = new ClientTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($client, $transformer, ENTITY_CLIENT); - - return $this->response($data); - } - + $data = $request->input(); $data['public_id'] = $publicId; - $this->clientRepo->save($data); + $client = $this->clientRepo->save($data); - $client = Client::scope($publicId) - ->with('country', 'contacts', 'industry', 'size', 'currency') - ->first(); - - if(!$client) - return $this->errorResponse(['message'=>'Client not found.'],400); - - $transformer = new ClientTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($client, $transformer, ENTITY_CLIENT); - - return $this->response($data); + return $this->itemResponse($client); } diff --git a/app/Http/Controllers/ExpenseApiController.php b/app/Http/Controllers/ExpenseApiController.php index 9b1a7e906244..44722f2a18ce 100644 --- a/app/Http/Controllers/ExpenseApiController.php +++ b/app/Http/Controllers/ExpenseApiController.php @@ -32,7 +32,7 @@ class ExpenseApiController extends BaseAPIController ->withTrashed() ->orderBy('created_at','desc'); - return $this->returnList($expenses); + return $this->listResponse($expenses); } public function update() diff --git a/app/Http/Controllers/InvoiceApiController.php b/app/Http/Controllers/InvoiceApiController.php index ce5eaa1d72d6..1ff6aa71faeb 100644 --- a/app/Http/Controllers/InvoiceApiController.php +++ b/app/Http/Controllers/InvoiceApiController.php @@ -62,7 +62,7 @@ class InvoiceApiController extends BaseAPIController ->with(array_merge(['invoice_items'], $this->getIncluded())) ->orderBy('created_at', 'desc'); - return $this->returnList($invoices); + return $this->listResponse($invoices); } /** diff --git a/app/Http/Controllers/PaymentApiController.php b/app/Http/Controllers/PaymentApiController.php index a7cab011fc36..e37e74971be3 100644 --- a/app/Http/Controllers/PaymentApiController.php +++ b/app/Http/Controllers/PaymentApiController.php @@ -12,6 +12,8 @@ use App\Ninja\Repositories\PaymentRepository; use App\Http\Controllers\BaseAPIController; use App\Ninja\Transformers\PaymentTransformer; use App\Ninja\Transformers\InvoiceTransformer; +use App\Http\Requests\UpdatePaymentRequest; +use App\Http\Requests\CreatePaymentAPIRequest; class PaymentApiController extends BaseAPIController { @@ -50,7 +52,7 @@ class PaymentApiController extends BaseAPIController ->with(array_merge(['client.contacts', 'invitation', 'user', 'invoice'], $this->getIncluded())) ->orderBy('created_at', 'desc'); - return $this->returnList($payments); + return $this->listResponse($payments); } /** @@ -75,39 +77,17 @@ class PaymentApiController extends BaseAPIController * ) */ - public function update(Request $request, $publicId) + public function update(UpdatePaymentRequest $request, $publicId) { - $data = Input::all(); - $data['public_id'] = $publicId; - $error = false; - - if ($request->action == ACTION_ARCHIVE) { - $payment = Payment::scope($publicId)->withTrashed()->firstOrFail(); - $this->paymentRepo->archive($payment); - - $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($payment, $transformer, 'invoice'); - - return $this->response($data); + if ($request->action) { + return $this->handleAction($request); } + $data = $request->input(); + $data['public_id'] = $publicId; $payment = $this->paymentRepo->save($data); - if ($error) { - return $error; - } - - /* - $invoice = Invoice::scope($data['invoice_id'])->with('client', 'invoice_items', 'invitations')->with(['payments' => function($query) { - $query->withTrashed(); - }])->withTrashed()->first(); - */ - - $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($payment, $transformer, 'invoice'); - - return $this->response($data); - + return $this->itemResponse($payment); } @@ -132,49 +112,15 @@ class PaymentApiController extends BaseAPIController * ) * ) */ - public function store() + public function store(CreatePaymentAPIRequest $request) { - $data = Input::all(); - $error = false; - - if (isset($data['invoice_id'])) { - $invoice = Invoice::scope($data['invoice_id'])->with('client')->first(); - - if ($invoice) { - $data['invoice_id'] = $invoice->id; - $data['client_id'] = $invoice->client->id; - } else { - $error = trans('validation.not_in', ['attribute' => 'invoice_id']); - } - } else { - $error = trans('validation.not_in', ['attribute' => 'invoice_id']); - } - - if (!isset($data['transaction_reference'])) { - $data['transaction_reference'] = ''; - } - - if ($error) { - return $error; - } - - $payment = $this->paymentRepo->save($data); + $payment = $this->paymentRepo->save($request->input()); if (Input::get('email_receipt')) { $this->contactMailer->sendPaymentConfirmation($payment); } - /* - $invoice = Invoice::scope($invoice->public_id)->with('client', 'invoice_items', 'invitations')->with(['payments' => function($query) { - $query->withTrashed(); - }])->first(); - */ - - $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($payment, $transformer, 'invoice'); - - return $this->response($data); - + return $this->itemResponse($payment); } /** diff --git a/app/Http/Controllers/ProductApiController.php b/app/Http/Controllers/ProductApiController.php index 26736f5ef5c2..1d3e4577048c 100644 --- a/app/Http/Controllers/ProductApiController.php +++ b/app/Http/Controllers/ProductApiController.php @@ -38,7 +38,7 @@ class ProductApiController extends BaseAPIController ->withTrashed() ->orderBy('created_at', 'desc'); - return $this->returnList($products); + return $this->listResponse($products); } public function getDatatable() diff --git a/app/Http/Controllers/TaskApiController.php b/app/Http/Controllers/TaskApiController.php index cb39e0a1f369..5e5e05ef80e9 100644 --- a/app/Http/Controllers/TaskApiController.php +++ b/app/Http/Controllers/TaskApiController.php @@ -45,7 +45,7 @@ class TaskApiController extends BaseAPIController ->with($this->getIncluded()) ->orderBy('created_at', 'desc'); - return $this->returnList($payments); + return $this->listResponse($payments); } /** diff --git a/app/Http/Controllers/TaxRateApiController.php b/app/Http/Controllers/TaxRateApiController.php index ddc0ab4aa968..1aa5417d9563 100644 --- a/app/Http/Controllers/TaxRateApiController.php +++ b/app/Http/Controllers/TaxRateApiController.php @@ -30,7 +30,7 @@ class TaxRateApiController extends BaseAPIController ->withTrashed() ->orderBy('created_at', 'desc'); - return $this->returnList($taxRates); + return $this->listResponse($taxRates); } public function store(CreateTaxRateRequest $request) diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index 8fda74b33cbd..6786e4046544 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -30,7 +30,7 @@ class UserApiController extends BaseAPIController ->withTrashed() ->orderBy('created_at', 'desc'); - return $this->returnList($users); + return $this->listResponse($users); } /* diff --git a/app/Http/Controllers/VendorApiController.php b/app/Http/Controllers/VendorApiController.php index b2487dfda2d3..1dea751e790d 100644 --- a/app/Http/Controllers/VendorApiController.php +++ b/app/Http/Controllers/VendorApiController.php @@ -53,7 +53,7 @@ class VendorApiController extends BaseAPIController ->withTrashed() ->orderBy('created_at', 'desc'); - return $this->returnList($vendors); + return $this->listResponse($vendors); } /** diff --git a/app/Http/Requests/CreatePaymentAPIRequest.php b/app/Http/Requests/CreatePaymentAPIRequest.php new file mode 100644 index 000000000000..08a520c16655 --- /dev/null +++ b/app/Http/Requests/CreatePaymentAPIRequest.php @@ -0,0 +1,48 @@ +user()->can('create', ENTITY_PAYMENT); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + if ( ! $this->invoice_id || ! $this->amount) { + return [ + 'invoice_id' => 'required', + 'amount' => 'required', + ]; + } + + $invoice = Invoice::scope($this->invoice_id)->firstOrFail(); + + $this->merge([ + 'invoice_id' => $invoice->id, + 'client_id' => $invoice->client->id, + ]); + + $rules = array( + 'amount' => "required|less_than:{$invoice->balance}|positive", + ); + + if ($this->payment_type_id == PAYMENT_TYPE_CREDIT) { + $rules['payment_type_id'] = 'has_credit:' . $invoice->client->public_id . ',' . $this->amount; + } + + return $rules; + } +} diff --git a/app/Http/routes.php b/app/Http/routes.php index 768c9c3d3d4c..386d02427e0b 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -246,7 +246,7 @@ Route::group([ // Route groups for API Route::group(['middleware' => 'api', 'prefix' => 'api/v1'], function() { - Route::get('ping', 'ClientApiController@ping'); + Route::get('ping', 'AccountApiController@ping'); Route::post('login', 'AccountApiController@login'); Route::post('register', 'AccountApiController@register'); Route::get('static', 'AccountApiController@getStaticData'); diff --git a/app/Models/EntityModel.php b/app/Models/EntityModel.php index c2c9110fe306..95e85e6acef4 100644 --- a/app/Models/EntityModel.php +++ b/app/Models/EntityModel.php @@ -110,7 +110,7 @@ class EntityModel extends Eloquent { return 'App\\Ninja\\Transformers\\' . ucwords(Utils::toCamelCase($entityType)) . 'Transformer'; } - + public function setNullValues() { foreach ($this->fillable as $field) { From e7f4368cbb3f8bb85142d346621be5c1b22f877c Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 16:12:37 +0300 Subject: [PATCH 041/120] Working on permissions in the API --- app/Http/Controllers/ProductApiController.php | 78 ++++--------------- app/Http/Controllers/TaxRateApiController.php | 48 +++++------- app/Http/Controllers/UserApiController.php | 5 -- app/Http/Controllers/VendorApiController.php | 4 +- app/Http/Requests/CreateTaxRateRequest.php | 4 +- app/Http/Requests/UpdateTaxRateRequest.php | 4 +- app/Http/Requests/UpdateUserRequest.php | 2 +- app/Libraries/Utils.php | 2 +- app/Models/Product.php | 8 ++ app/Ninja/Repositories/ProductRepository.php | 18 +++++ app/Providers/AuthServiceProvider.php | 2 + 11 files changed, 70 insertions(+), 105 deletions(-) diff --git a/app/Http/Controllers/ProductApiController.php b/app/Http/Controllers/ProductApiController.php index 1d3e4577048c..5bf1b239e4c0 100644 --- a/app/Http/Controllers/ProductApiController.php +++ b/app/Http/Controllers/ProductApiController.php @@ -1,34 +1,20 @@ productService = $productService; $this->productRepo = $productRepo; } @@ -41,58 +27,28 @@ class ProductApiController extends BaseAPIController return $this->listResponse($products); } - public function getDatatable() + public function store(CreateProductRequest $request) { - return $this->productService->getDatatable(Auth::user()->account_id); + $product = $this->productRepo->save($request->input()); + + return $this->itemResponse($product); } - public function store() + public function update(UpdateProductRequest $request, $publicId) { - return $this->save(); - } - - public function update(\Illuminate\Http\Request $request, $publicId) - { - - if ($request->action == ACTION_ARCHIVE) { - $product = Product::scope($publicId)->withTrashed()->firstOrFail(); - $this->productRepo->archive($product); - - $transformer = new ProductTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($product, $transformer, 'products'); - - return $this->response($data); + if ($request->action) { + return $this->handleAction($request); } - else - return $this->save($publicId); + + $data = $request->input(); + $data['public_id'] = $publicId; + $product = $this->productRepo->save($data); + + return $this->itemResponse($product); } public function destroy($publicId) { //stub } - - private function save($productPublicId = false) - { - if ($productPublicId) { - $product = Product::scope($productPublicId)->firstOrFail(); - } else { - $product = Product::createNew(); - } - - $product->product_key = trim(Input::get('product_key')); - $product->notes = trim(Input::get('notes')); - $product->cost = trim(Input::get('cost')); - //$product->default_tax_rate_id = Input::get('default_tax_rate_id'); - - $product->save(); - - $transformer = new ProductTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($product, $transformer, 'products'); - - return $this->response($data); - - } - - } diff --git a/app/Http/Controllers/TaxRateApiController.php b/app/Http/Controllers/TaxRateApiController.php index 1aa5417d9563..1c4e9bf255bc 100644 --- a/app/Http/Controllers/TaxRateApiController.php +++ b/app/Http/Controllers/TaxRateApiController.php @@ -1,26 +1,20 @@ taxRateService = $taxRateService; $this->taxRateRepo = $taxRateRepo; } @@ -29,38 +23,32 @@ class TaxRateApiController extends BaseAPIController $taxRates = TaxRate::scope() ->withTrashed() ->orderBy('created_at', 'desc'); - + return $this->listResponse($taxRates); } public function store(CreateTaxRateRequest $request) { - return $this->save($request); + $taxRate = $this->taxRateRepo->save($request->input()); + + return $this->itemResponse($taxRate); } - public function update(UpdateTaxRateRequest $request, $taxRatePublicId) + public function update(UpdateTaxRateRequest $request, $publicId) { - $taxRate = TaxRate::scope($taxRatePublicId)->firstOrFail(); - - if ($request->action == ACTION_ARCHIVE) { - $this->taxRateRepo->archive($taxRate); - - $transformer = new TaxRateTransformer(Auth::user()->account, $request->serializer); - $data = $this->createItem($taxRate, $transformer, 'tax_rates'); - - return $this->response($data); - } else { - return $this->save($request, $taxRate); + if ($request->action) { + return $this->handleAction($request); } + + $data = $request->input(); + $data['public_id'] = $publicId; + $taxRate = $this->taxRateRepo->save($data); + + return $this->itemResponse($taxRate); } - private function save($request, $taxRate = false) + public function destroy($publicId) { - $taxRate = $this->taxRateRepo->save($request->input(), $taxRate); - - $transformer = new TaxRateTransformer(\Auth::user()->account, $request->serializer); - $data = $this->createItem($taxRate, $transformer, 'tax_rates'); - - return $this->response($data); + //stub } } diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index 6786e4046544..2869c3512f5a 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -42,11 +42,6 @@ class UserApiController extends BaseAPIController public function update(UpdateUserRequest $request, $userPublicId) { - /* - // temporary fix for ids starting at 0 - $userPublicId -= 1; - $user = User::scope($userPublicId)->firstOrFail(); - */ $user = Auth::user(); if ($request->action == ACTION_ARCHIVE) { diff --git a/app/Http/Controllers/VendorApiController.php b/app/Http/Controllers/VendorApiController.php index 1dea751e790d..d3cc27094760 100644 --- a/app/Http/Controllers/VendorApiController.php +++ b/app/Http/Controllers/VendorApiController.php @@ -85,8 +85,6 @@ class VendorApiController extends BaseAPIController ->with('country', 'vendorcontacts', 'industry', 'size', 'currency') ->first(); - $transformer = new VendorTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($vendor, $transformer, ENTITY_VENDOR); - return $this->response($data); + return $this->itemResponse($vendor); } } diff --git a/app/Http/Requests/CreateTaxRateRequest.php b/app/Http/Requests/CreateTaxRateRequest.php index 1596c814f127..d8fef50093b7 100644 --- a/app/Http/Requests/CreateTaxRateRequest.php +++ b/app/Http/Requests/CreateTaxRateRequest.php @@ -3,7 +3,7 @@ use App\Http\Requests\Request; use Illuminate\Validation\Factory; -class CreateTaxRateRequest extends Request +class CreateTaxRateRequest extends TaxRateRequest { // Expenses /** @@ -13,7 +13,7 @@ class CreateTaxRateRequest extends Request */ public function authorize() { - return true; + return $this->user()->can('create', ENTITY_TAX_RATE); } /** diff --git a/app/Http/Requests/UpdateTaxRateRequest.php b/app/Http/Requests/UpdateTaxRateRequest.php index bcfa298e06c5..a4bdc6301ca2 100644 --- a/app/Http/Requests/UpdateTaxRateRequest.php +++ b/app/Http/Requests/UpdateTaxRateRequest.php @@ -3,7 +3,7 @@ use App\Http\Requests\Request; use Illuminate\Validation\Factory; -class UpdateTaxRateRequest extends Request +class UpdateTaxRateRequest extends TaxRateRequest { // Expenses /** @@ -13,7 +13,7 @@ class UpdateTaxRateRequest extends Request */ public function authorize() { - return true; + return $this->user()->can('edit', $this->entity()); } /** diff --git a/app/Http/Requests/UpdateUserRequest.php b/app/Http/Requests/UpdateUserRequest.php index 91d7a73bc568..b3149d2b61ff 100644 --- a/app/Http/Requests/UpdateUserRequest.php +++ b/app/Http/Requests/UpdateUserRequest.php @@ -14,7 +14,7 @@ class UpdateUserRequest extends Request */ public function authorize() { - return true; + return $this->user()->can('edit', $this->entity()); } /** diff --git a/app/Libraries/Utils.php b/app/Libraries/Utils.php index 86107acfcd59..141eacfc3058 100644 --- a/app/Libraries/Utils.php +++ b/app/Libraries/Utils.php @@ -676,7 +676,7 @@ class Utils public static function getEntityName($entityType) { - return ucwords(str_replace('_', ' ', $entityType)); + return ucwords(Utils::toCamelCase($entityType)); } public static function getClientDisplayName($model) diff --git a/app/Models/Product.php b/app/Models/Product.php index 8de7c7ac5b2c..0d3221f2a1ea 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -8,6 +8,14 @@ class Product extends EntityModel use SoftDeletes; protected $dates = ['deleted_at']; + protected $fillable = [ + 'product_key', + 'notes', + 'cost', + 'qty', + 'default_tax_rate_id', + ]; + public function getEntityType() { return ENTITY_PRODUCT; diff --git a/app/Ninja/Repositories/ProductRepository.php b/app/Ninja/Repositories/ProductRepository.php index 417b49f23640..7100b083a553 100644 --- a/app/Ninja/Repositories/ProductRepository.php +++ b/app/Ninja/Repositories/ProductRepository.php @@ -1,6 +1,7 @@ firstOrFail(); + } else { + $product = Product::createNew(); + } + + $product->fill($data); + $product->save(); + + return $product; + } + } \ No newline at end of file diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 884c2587503d..02aba3e1d6d3 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -21,6 +21,8 @@ class AuthServiceProvider extends ServiceProvider \App\Models\Payment::class => \App\Policies\PaymentPolicy::class, \App\Models\Task::class => \App\Policies\TaskPolicy::class, \App\Models\Vendor::class => \App\Policies\VendorPolicy::class, + \App\Models\Product::class => \App\Policies\ProductPolicy::class, + \App\Models\TaxRate::class => \App\Policies\TaxRatePolicy::class, ]; /** From 6acddfc3c7fc8d1f753e123e1a4e13072bdd482a Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 16:45:12 +0300 Subject: [PATCH 042/120] Working on permissions in the API --- app/Http/Controllers/AppController.php | 2 +- app/Http/Controllers/InvoiceApiController.php | 90 +++++-------------- app/Http/Controllers/PaymentApiController.php | 5 -- app/Http/Requests/CreateProductRequest.php | 26 ++++++ app/Http/Requests/ProductRequest.php | 6 ++ app/Http/Requests/TaxRateRequest.php | 7 ++ app/Http/Requests/UpdateProductRequest.php | 26 ++++++ 7 files changed, 90 insertions(+), 72 deletions(-) create mode 100644 app/Http/Requests/CreateProductRequest.php create mode 100644 app/Http/Requests/ProductRequest.php create mode 100644 app/Http/Requests/TaxRateRequest.php create mode 100644 app/Http/Requests/UpdateProductRequest.php diff --git a/app/Http/Controllers/AppController.php b/app/Http/Controllers/AppController.php index 9dcf243fbe02..568cb4d45f5b 100644 --- a/app/Http/Controllers/AppController.php +++ b/app/Http/Controllers/AppController.php @@ -306,7 +306,7 @@ class AppController extends BaseController public function stats() { - if (Input::get('password') != env('RESELLER_PASSWORD')) { + if ( ! hash_equals(Input::get('password'), env('RESELLER_PASSWORD'))) { sleep(3); return ''; } diff --git a/app/Http/Controllers/InvoiceApiController.php b/app/Http/Controllers/InvoiceApiController.php index 1ff6aa71faeb..e4cc870f210c 100644 --- a/app/Http/Controllers/InvoiceApiController.php +++ b/app/Http/Controllers/InvoiceApiController.php @@ -18,6 +18,7 @@ use App\Ninja\Repositories\InvoiceRepository; use App\Ninja\Mailers\ContactMailer as Mailer; use App\Http\Controllers\BaseAPIController; use App\Ninja\Transformers\InvoiceTransformer; +use App\Http\Requests\InvoiceRequest; use App\Http\Requests\CreateInvoiceAPIRequest; use App\Http\Requests\UpdateInvoiceAPIRequest; use App\Services\InvoiceService; @@ -82,17 +83,9 @@ class InvoiceApiController extends BaseAPIController * ) */ - public function show($publicId) + public function show(InvoiceRequest $request) { - $invoice = Invoice::scope($publicId)->withTrashed()->first(); - - if(!$invoice) - return $this->errorResponse(['message'=>'Invoice does not exist!'], 404); - - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); + return $this->itemResponse($request->entity()); } /** @@ -187,11 +180,11 @@ class InvoiceApiController extends BaseAPIController } } - $invoice = Invoice::scope($invoice->public_id)->with('client', 'invoice_items', 'invitations')->first(); - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); + $invoice = Invoice::scope($invoice->public_id) + ->with('client', 'invoice_items', 'invitations') + ->first(); + + return $this->itemResponse($invoice); } private function prepareData($data, $client) @@ -277,36 +270,21 @@ class InvoiceApiController extends BaseAPIController $item[$key] = $val; } } - + return $item; } - public function emailInvoice() + public function emailInvoice(InvoiceRequest $request) { - $data = Input::all(); - $error = null; + $invoice = $request->entity(); - $invoice = Invoice::scope($data['id'])->withTrashed()->first(); - - if(!$invoice) - return $this->errorResponse(['message'=>'Invoice does not exist.'], 400); - - - $this->mailer->sendInvoice($invoice, false, false); - - - if($error) { - return $this->errorResponse(['message'=>'There was an error sending the invoice'], 400); - } - else { - $response = json_encode(RESULT_SUCCESS, JSON_PRETTY_PRINT); - } + $this->mailer->sendInvoice($invoice); + $response = json_encode(RESULT_SUCCESS, JSON_PRETTY_PRINT); $headers = Utils::getApiHeaders(); - return Response::make($response, $error ? 400 : 200, $headers); + return Response::make($response, 200, $headers); } - /** * @SWG\Put( * path="/invoices", @@ -330,43 +308,23 @@ class InvoiceApiController extends BaseAPIController */ public function update(UpdateInvoiceAPIRequest $request, $publicId) { - if ($request->action == ACTION_ARCHIVE) { - $invoice = Invoice::scope($publicId)->firstOrFail(); - $this->invoiceRepo->archive($invoice); - - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); - } - else if ($request->action == ACTION_CONVERT) { - $quote = Invoice::scope($publicId)->firstOrFail(); + if ($request->action == ACTION_CONVERT) { + $quote = $request->entity(); $invoice = $this->invoiceRepo->cloneInvoice($quote, $quote->id); - - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); - } - else if ($request->action == ACTION_RESTORE) { - $invoice = Invoice::scope($publicId)->withTrashed()->firstOrFail(); - $this->invoiceRepo->restore($invoice); - - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); + return $this->itemResponse($invoice); + } elseif ($request->action) { + return $this->handleAction($request); } $data = $request->input(); $data['public_id'] = $publicId; $this->invoiceService->save($data); - $invoice = Invoice::scope($publicId)->with('client', 'invoice_items', 'invitations')->firstOrFail(); - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); + $invoice = Invoice::scope($publicId) + ->with('client', 'invoice_items', 'invitations') + ->firstOrFail(); + + return $this->itemResponse($invoice); } /** diff --git a/app/Http/Controllers/PaymentApiController.php b/app/Http/Controllers/PaymentApiController.php index e37e74971be3..c1d0fd8ae0b6 100644 --- a/app/Http/Controllers/PaymentApiController.php +++ b/app/Http/Controllers/PaymentApiController.php @@ -153,11 +153,6 @@ class PaymentApiController extends BaseAPIController $this->paymentRepo->delete($payment); - /* - $invoice = Invoice::scope($invoiceId)->with('client', 'invoice_items', 'invitations')->with(['payments' => function($query) { - $query->withTrashed(); - }])->first(); - */ $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); $data = $this->createItem($payment, $transformer, 'invoice'); diff --git a/app/Http/Requests/CreateProductRequest.php b/app/Http/Requests/CreateProductRequest.php new file mode 100644 index 000000000000..0fad2af14a23 --- /dev/null +++ b/app/Http/Requests/CreateProductRequest.php @@ -0,0 +1,26 @@ +user()->can('create', ENTITY_PRODUCT); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + 'product_key' => 'required', + ]; + } +} diff --git a/app/Http/Requests/ProductRequest.php b/app/Http/Requests/ProductRequest.php new file mode 100644 index 000000000000..2420bdcfcaf0 --- /dev/null +++ b/app/Http/Requests/ProductRequest.php @@ -0,0 +1,6 @@ +user()->can('edit', $this->entity()); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + 'product_key' => 'required', + ]; + } +} From 325cac1603a81e6d271603d8a8eeb9a65cb24b1d Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 20:02:15 +0300 Subject: [PATCH 043/120] Added DocumentTransformer --- .../Transformers/DocumentTransformer.php | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 app/Ninja/Transformers/DocumentTransformer.php diff --git a/app/Ninja/Transformers/DocumentTransformer.php b/app/Ninja/Transformers/DocumentTransformer.php new file mode 100644 index 000000000000..61d18506eba1 --- /dev/null +++ b/app/Ninja/Transformers/DocumentTransformer.php @@ -0,0 +1,21 @@ + (int) $document->public_id, + 'name' => $document->name, + 'account_key' => $this->account->account_key, + 'type' => $document->type, + 'invoice_id' => isset($document->invoice->public_id) ? (int) $document->invoice->public_id : null, + 'expense_id' => isset($document->expense->public_id) ? (int) $document->expense->public_id : null, + ]; + } +} \ No newline at end of file From e6bcdb36b38979f6d592c4ee92a786d46921e6db Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 20:42:13 +0300 Subject: [PATCH 044/120] Removed extra entity load in the repos save function --- app/Http/Controllers/BaseController.php | 24 ------------------- app/Http/Controllers/ClientApiController.php | 2 +- app/Http/Controllers/ExpenseController.php | 2 +- app/Http/Controllers/InvoiceApiController.php | 2 +- app/Http/Controllers/InvoiceController.php | 10 +++----- app/Http/Controllers/PaymentApiController.php | 2 +- app/Http/Controllers/PaymentController.php | 2 +- app/Http/Controllers/ProductApiController.php | 2 +- app/Http/Controllers/TaxRateApiController.php | 2 +- app/Http/Controllers/TaxRateController.php | 4 +--- app/Http/Controllers/VendorController.php | 2 +- app/Ninja/Repositories/ClientRepository.php | 5 ++-- app/Ninja/Repositories/CreditRepository.php | 9 ++++--- app/Ninja/Repositories/ExpenseRepository.php | 7 ++++-- app/Ninja/Repositories/InvoiceRepository.php | 7 ++++-- app/Ninja/Repositories/PaymentRepository.php | 7 ++++-- app/Ninja/Repositories/ProductRepository.php | 7 ++++-- app/Ninja/Repositories/TaskRepository.php | 7 ++++-- app/Ninja/Repositories/TaxRateRepository.php | 15 ++++++------ app/Ninja/Repositories/VendorRepository.php | 7 ++++-- app/Services/ExpenseService.php | 4 ++-- app/Services/InvoiceService.php | 4 ++-- app/Services/VendorService.php | 4 ++-- 23 files changed, 65 insertions(+), 72 deletions(-) diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index 4a33946c7631..2ce7a633f179 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -24,28 +24,4 @@ class BaseController extends Controller $this->layout = View::make($this->layout); } } - - protected function authorizeCreate() { - $this->authorize('create', $this->entityType); - } - - /* - protected function authorizeUpdate($entity) { - $this->authorize('edit', $entity); - } - */ - - protected function authorizeUpdate($input){ - $creating = empty($input['public_id']) || $input['public_id'] == '-1'; - - if($creating){ - $this->authorize('create', $this->entityType); - } - else{ - $className = Utils::getEntityName($this->entityType); - - $object = call_user_func(array("App\\Models\\{$className}", 'scope'), $input['public_id'])->firstOrFail(); - $this->authorize('edit', $object); - } - } } diff --git a/app/Http/Controllers/ClientApiController.php b/app/Http/Controllers/ClientApiController.php index 83689e274318..ba6b56c952fa 100644 --- a/app/Http/Controllers/ClientApiController.php +++ b/app/Http/Controllers/ClientApiController.php @@ -116,7 +116,7 @@ class ClientApiController extends BaseAPIController $data = $request->input(); $data['public_id'] = $publicId; - $client = $this->clientRepo->save($data); + $client = $this->clientRepo->save($data, $request->entity()); return $this->itemResponse($client); } diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index 406613489c6b..3da123c2d972 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -146,7 +146,7 @@ class ExpenseController extends BaseController $data = $request->input(); $data['documents'] = $request->file('documents'); - $expense = $this->expenseService->save($data); + $expense = $this->expenseService->save($data, $request->entity()); Session::flash('message', trans('texts.updated_expense')); diff --git a/app/Http/Controllers/InvoiceApiController.php b/app/Http/Controllers/InvoiceApiController.php index e4cc870f210c..32d38cc9316d 100644 --- a/app/Http/Controllers/InvoiceApiController.php +++ b/app/Http/Controllers/InvoiceApiController.php @@ -318,7 +318,7 @@ class InvoiceApiController extends BaseAPIController $data = $request->input(); $data['public_id'] = $publicId; - $this->invoiceService->save($data); + $this->invoiceService->save($data, $request->entity()); $invoice = Invoice::scope($publicId) ->with('client', 'invoice_items', 'invitations') diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 1a652ae8962c..270cc10b96ac 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -396,9 +396,7 @@ class InvoiceController extends BaseController { $data = $request->input(); $data['documents'] = $request->file('documents'); - - $this->authorizeUpdate($data); - + $action = Input::get('action'); $entityType = Input::get('entityType'); @@ -435,13 +433,11 @@ class InvoiceController extends BaseController { $data = $request->input(); $data['documents'] = $request->file('documents'); - - $this->authorizeUpdate($data); - + $action = Input::get('action'); $entityType = Input::get('entityType'); - $invoice = $this->invoiceService->save($data); + $invoice = $this->invoiceService->save($data, $request->entity()); $entityType = $invoice->getEntityType(); $message = trans("texts.updated_{$entityType}"); Session::flash('message', $message); diff --git a/app/Http/Controllers/PaymentApiController.php b/app/Http/Controllers/PaymentApiController.php index c1d0fd8ae0b6..5cea7f23641a 100644 --- a/app/Http/Controllers/PaymentApiController.php +++ b/app/Http/Controllers/PaymentApiController.php @@ -85,7 +85,7 @@ class PaymentApiController extends BaseAPIController $data = $request->input(); $data['public_id'] = $publicId; - $payment = $this->paymentRepo->save($data); + $payment = $this->paymentRepo->save($data, $request->entity()); return $this->itemResponse($payment); } diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index e3307f6f47e5..12537dbebaa1 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -603,7 +603,7 @@ class PaymentController extends BaseController public function update(UpdatePaymentRequest $request) { - $payment = $this->paymentRepo->save($request->input()); + $payment = $this->paymentRepo->save($request->input(), $request->entity()); Session::flash('message', trans('texts.updated_payment')); diff --git a/app/Http/Controllers/ProductApiController.php b/app/Http/Controllers/ProductApiController.php index 5bf1b239e4c0..6a8756eda4b6 100644 --- a/app/Http/Controllers/ProductApiController.php +++ b/app/Http/Controllers/ProductApiController.php @@ -42,7 +42,7 @@ class ProductApiController extends BaseAPIController $data = $request->input(); $data['public_id'] = $publicId; - $product = $this->productRepo->save($data); + $product = $this->productRepo->save($data, $request->entity()); return $this->itemResponse($product); } diff --git a/app/Http/Controllers/TaxRateApiController.php b/app/Http/Controllers/TaxRateApiController.php index 1c4e9bf255bc..85756205d9a1 100644 --- a/app/Http/Controllers/TaxRateApiController.php +++ b/app/Http/Controllers/TaxRateApiController.php @@ -42,7 +42,7 @@ class TaxRateApiController extends BaseAPIController $data = $request->input(); $data['public_id'] = $publicId; - $taxRate = $this->taxRateRepo->save($data); + $taxRate = $this->taxRateRepo->save($data, $request->entity()); return $this->itemResponse($taxRate); } diff --git a/app/Http/Controllers/TaxRateController.php b/app/Http/Controllers/TaxRateController.php index 223f7491092e..cba4058756de 100644 --- a/app/Http/Controllers/TaxRateController.php +++ b/app/Http/Controllers/TaxRateController.php @@ -75,9 +75,7 @@ class TaxRateController extends BaseController public function update(UpdateTaxRateRequest $request, $publicId) { - $taxRate = TaxRate::scope($publicId)->firstOrFail(); - - $this->taxRateRepo->save($request->input(), $taxRate); + $this->taxRateRepo->save($request->input(), $request->entity()); Session::flash('message', trans('texts.updated_tax_rate')); return Redirect::to('settings/' . ACCOUNT_TAX_RATES); diff --git a/app/Http/Controllers/VendorController.php b/app/Http/Controllers/VendorController.php index 10c7f7f03e89..f1952e20dee3 100644 --- a/app/Http/Controllers/VendorController.php +++ b/app/Http/Controllers/VendorController.php @@ -182,7 +182,7 @@ class VendorController extends BaseController */ public function update(UpdateVendorRequest $request) { - $vendor = $this->vendorService->save($request->input()); + $vendor = $this->vendorService->save($request->input(), $request->entity()); Session::flash('message', trans('texts.updated_vendor')); diff --git a/app/Ninja/Repositories/ClientRepository.php b/app/Ninja/Repositories/ClientRepository.php index 77ebcb7fd736..8d8c3ed527d4 100644 --- a/app/Ninja/Repositories/ClientRepository.php +++ b/app/Ninja/Repositories/ClientRepository.php @@ -72,12 +72,13 @@ class ClientRepository extends BaseRepository if ($client) { // do nothing - } if (!$publicId || $publicId == '-1') { + } elseif (!$publicId || $publicId == '-1') { $client = Client::createNew(); } else { $client = Client::scope($publicId)->with('contacts')->firstOrFail(); + \Log::warning('Entity not set in client repo save'); } - + // convert currency code to id if (isset($data['currency_code'])) { $currencyCode = strtolower($data['currency_code']); diff --git a/app/Ninja/Repositories/CreditRepository.php b/app/Ninja/Repositories/CreditRepository.php index 4381ae25771c..068707ace02b 100644 --- a/app/Ninja/Repositories/CreditRepository.php +++ b/app/Ninja/Repositories/CreditRepository.php @@ -59,12 +59,15 @@ class CreditRepository extends BaseRepository return $query; } - public function save($input) + public function save($input, $credit = null) { $publicId = isset($data['public_id']) ? $data['public_id'] : false; - - if ($publicId) { + + if ($credit) { + // do nothing + } elseif ($publicId) { $credit = Credit::scope($publicId)->firstOrFail(); + \Log::warning('Entity not set in credit repo save'); } else { $credit = Credit::createNew(); } diff --git a/app/Ninja/Repositories/ExpenseRepository.php b/app/Ninja/Repositories/ExpenseRepository.php index 4c67c0e7c91c..442af74bd32b 100644 --- a/app/Ninja/Repositories/ExpenseRepository.php +++ b/app/Ninja/Repositories/ExpenseRepository.php @@ -122,12 +122,15 @@ class ExpenseRepository extends BaseRepository return $query; } - public function save($input) + public function save($input, $expense = null) { $publicId = isset($input['public_id']) ? $input['public_id'] : false; - if ($publicId) { + if ($expense) { + // do nothing + } elseif ($publicId) { $expense = Expense::scope($publicId)->firstOrFail(); + \Log::warning('Entity not set in expense repo save'); } else { $expense = Expense::createNew(); } diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index 2ae18a738bd8..bbf58ca16c71 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -201,14 +201,16 @@ class InvoiceRepository extends BaseRepository ->make(); } - public function save($data) + public function save($data, $invoice = null) { $account = \Auth::user()->account; $publicId = isset($data['public_id']) ? $data['public_id'] : false; $isNew = !$publicId || $publicId == '-1'; - if ($isNew) { + if ($invoice) { + // do nothing + } elseif ($isNew) { $entityType = ENTITY_INVOICE; if (isset($data['is_recurring']) && filter_var($data['is_recurring'], FILTER_VALIDATE_BOOLEAN)) { $entityType = ENTITY_RECURRING_INVOICE; @@ -224,6 +226,7 @@ class InvoiceRepository extends BaseRepository } } else { $invoice = Invoice::scope($publicId)->firstOrFail(); + \Log::warning('Entity not set in invoice repo save'); } $invoice->fill($data); diff --git a/app/Ninja/Repositories/PaymentRepository.php b/app/Ninja/Repositories/PaymentRepository.php index 9b7d9787b457..dd99fccdfdd4 100644 --- a/app/Ninja/Repositories/PaymentRepository.php +++ b/app/Ninja/Repositories/PaymentRepository.php @@ -123,12 +123,15 @@ class PaymentRepository extends BaseRepository return $query; } - public function save($input) + public function save($input, $payment = null) { $publicId = isset($input['public_id']) ? $input['public_id'] : false; - if ($publicId) { + if ($payment) { + // do nothing + } elseif ($publicId) { $payment = Payment::scope($publicId)->firstOrFail(); + \Log::warning('Entity not set in payment repo save'); } else { $payment = Payment::createNew(); } diff --git a/app/Ninja/Repositories/ProductRepository.php b/app/Ninja/Repositories/ProductRepository.php index 7100b083a553..eb0e7383e9e5 100644 --- a/app/Ninja/Repositories/ProductRepository.php +++ b/app/Ninja/Repositories/ProductRepository.php @@ -31,12 +31,15 @@ class ProductRepository extends BaseRepository ); } - public function save($data) + public function save($data, $product = null) { $publicId = isset($data['public_id']) ? $data['public_id'] : false; - if ($publicId) { + if ($product) { + // do nothing + } elseif ($publicId) { $product = Product::scope($publicId)->firstOrFail(); + \Log::warning('Entity not set in product repo save'); } else { $product = Product::createNew(); } diff --git a/app/Ninja/Repositories/TaskRepository.php b/app/Ninja/Repositories/TaskRepository.php index 1bd0e38cb6b7..3a5eca04a112 100644 --- a/app/Ninja/Repositories/TaskRepository.php +++ b/app/Ninja/Repositories/TaskRepository.php @@ -64,10 +64,13 @@ class TaskRepository return $query; } - public function save($publicId, $data) + public function save($publicId, $data, $task = null) { - if ($publicId) { + if ($task) { + // do nothing + } elseif ($publicId) { $task = Task::scope($publicId)->firstOrFail(); + \Log::warning('Entity not set in task repo save'); } else { $task = Task::createNew(); } diff --git a/app/Ninja/Repositories/TaxRateRepository.php b/app/Ninja/Repositories/TaxRateRepository.php index 2e325100a016..8f1ad7f6550f 100644 --- a/app/Ninja/Repositories/TaxRateRepository.php +++ b/app/Ninja/Repositories/TaxRateRepository.php @@ -20,14 +20,15 @@ class TaxRateRepository extends BaseRepository ->select('tax_rates.public_id', 'tax_rates.name', 'tax_rates.rate', 'tax_rates.deleted_at'); } - public function save($data, $taxRate = false) + public function save($data, $taxRate = null) { - if ( ! $taxRate) { - if (isset($data['public_id'])) { - $taxRate = TaxRate::scope($data['public_id'])->firstOrFail(); - } else { - $taxRate = TaxRate::createNew(); - } + if ($taxRate) { + // do nothing + } elseif (isset($data['public_id'])) { + $taxRate = TaxRate::scope($data['public_id'])->firstOrFail(); + \Log::warning('Entity not set in tax rate repo save'); + } else { + $taxRate = TaxRate::createNew(); } $taxRate->fill($data); diff --git a/app/Ninja/Repositories/VendorRepository.php b/app/Ninja/Repositories/VendorRepository.php index df885f62e12e..ef5648f47d81 100644 --- a/app/Ninja/Repositories/VendorRepository.php +++ b/app/Ninja/Repositories/VendorRepository.php @@ -62,14 +62,17 @@ class VendorRepository extends BaseRepository return $query; } - public function save($data) + public function save($data, $vendor = null) { $publicId = isset($data['public_id']) ? $data['public_id'] : false; - if (!$publicId || $publicId == '-1') { + if ($vendor) { + // do nothing + } elseif (!$publicId || $publicId == '-1') { $vendor = Vendor::createNew(); } else { $vendor = Vendor::scope($publicId)->with('vendorcontacts')->firstOrFail(); + \Log::warning('Entity not set in vendor repo save'); } $vendor->fill($data); diff --git a/app/Services/ExpenseService.php b/app/Services/ExpenseService.php index 0b28a7c4d6b1..671648ea32a5 100644 --- a/app/Services/ExpenseService.php +++ b/app/Services/ExpenseService.php @@ -28,7 +28,7 @@ class ExpenseService extends BaseService return $this->expenseRepo; } - public function save($data) + public function save($data, $expense = null) { if (isset($data['client_id']) && $data['client_id']) { $data['client_id'] = Client::getPrivateId($data['client_id']); @@ -38,7 +38,7 @@ class ExpenseService extends BaseService $data['vendor_id'] = Vendor::getPrivateId($data['vendor_id']); } - return $this->expenseRepo->save($data); + return $this->expenseRepo->save($data, $expense); } public function getDatatable($search) diff --git a/app/Services/InvoiceService.php b/app/Services/InvoiceService.php index ecf0ad2fab0b..edbee8caf84c 100644 --- a/app/Services/InvoiceService.php +++ b/app/Services/InvoiceService.php @@ -30,7 +30,7 @@ class InvoiceService extends BaseService return $this->invoiceRepo; } - public function save($data) + public function save($data, $invoice = null) { if (isset($data['client'])) { $canSaveClient = false; @@ -46,7 +46,7 @@ class InvoiceService extends BaseService } } - $invoice = $this->invoiceRepo->save($data); + $invoice = $this->invoiceRepo->save($data, $invoice); $client = $invoice->client; $client->load('contacts'); diff --git a/app/Services/VendorService.php b/app/Services/VendorService.php index 6022507b452e..41f5fd4664bb 100644 --- a/app/Services/VendorService.php +++ b/app/Services/VendorService.php @@ -26,13 +26,13 @@ class VendorService extends BaseService return $this->vendorRepo; } - public function save($data) + public function save($data, $vendor = null) { if (Auth::user()->account->isNinjaAccount() && isset($data['plan'])) { $this->ninjaRepo->updatePlanDetails($data['public_id'], $data); } - return $this->vendorRepo->save($data); + return $this->vendorRepo->save($data, $vendor); } public function getDatatable($search) From 29a03179a9e2be0cfd6fcf1fb009558a6b71022f Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 22:34:30 +0300 Subject: [PATCH 045/120] Fix for travis tests --- tests/acceptance/ExpenseCest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/acceptance/ExpenseCest.php b/tests/acceptance/ExpenseCest.php index 01cc3112d83f..c32d21d7a4d0 100644 --- a/tests/acceptance/ExpenseCest.php +++ b/tests/acceptance/ExpenseCest.php @@ -49,7 +49,7 @@ class ExpenseCest // invoice expense $I->executeJS('submitAction(\'invoice\')'); $I->click('Save'); - $I->wait(1); + $I->wait(2); $I->see($clientEmail); $I->see($amount); } From f74b30cddecf4ed8c24f1311cccd5420c6539a35 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 22:51:22 +0300 Subject: [PATCH 046/120] Added automated tests for API --- .../_support/_generated/AcceptanceTesterActions.php | 2 +- tests/acceptance/APICest.php | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/_support/_generated/AcceptanceTesterActions.php b/tests/_support/_generated/AcceptanceTesterActions.php index d219111f887c..82427d1e2a5e 100644 --- a/tests/_support/_generated/AcceptanceTesterActions.php +++ b/tests/_support/_generated/AcceptanceTesterActions.php @@ -1,4 +1,4 @@ -createEntity('tax_rate', $data); $this->listEntities('tax_rate'); + $data = new stdClass; + $data->product_key = $this->faker->word; + $data->notes = $this->faker->realText(100); + $this->createEntity('product', $data); + $this->listEntities('product'); + + $data = new stdClass; + $data->name = $this->faker->word; + $data->vendorcontacts = []; + $this->createEntity('vendor', $data); + $this->listEntities('vendor'); + $this->listEntities('account'); } @@ -79,6 +91,7 @@ class APICest $response = $this->sendRequest("{$entityType}s", $data); $entityId = $response->data->id; + PHPUnit_Framework_Assert::assertGreaterThan(0, $entityId); return $entityId; From 65d70c620ef88731a6de679b5ad4c88ade439786 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 23:06:36 +0300 Subject: [PATCH 047/120] Enabled setting per_page for API results --- app/Http/Controllers/BaseAPIController.php | 3 ++- app/Http/routes.php | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index 7bfdfc931c90..489053d055f0 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -134,8 +134,9 @@ class BaseAPIController extends Controller } if (is_a($query, "Illuminate\Database\Eloquent\Builder")) { + $limit = min(MAX_API_PAGE_SIZE, Input::get('per_page', DEFAULT_API_PAGE_SIZE)); $resource = new Collection($query->get(), $transformer, $entityType); - $resource->setPaginator(new IlluminatePaginatorAdapter($query->paginate())); + $resource->setPaginator(new IlluminatePaginatorAdapter($query->paginate($limit))); } else { $resource = new Collection($query, $transformer, $entityType); } diff --git a/app/Http/routes.php b/app/Http/routes.php index 386d02427e0b..df1f1add4bb8 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -599,6 +599,8 @@ if (!defined('CONTACT_EMAIL')) { define('TEST_USERNAME', 'user@example.com'); define('TEST_PASSWORD', 'password'); define('API_SECRET', 'API_SECRET'); + define('DEFAULT_API_PAGE_SIZE', 15); + define('MAX_API_PAGE_SIZE', 100); define('IOS_PRODUCTION_PUSH', env('IOS_PRODUCTION_PUSH', 'ninjaIOS')); define('IOS_DEV_PUSH', env('IOS_DEV_PUSH', 'devNinjaIOS')); From 95fdda6e1b06f53c6ba89d9e02a28f7f5d32bffa Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 May 2016 23:11:16 +0300 Subject: [PATCH 048/120] Fix for travis tests --- tests/acceptance/ExpenseCest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/acceptance/ExpenseCest.php b/tests/acceptance/ExpenseCest.php index c32d21d7a4d0..72d7666aab9d 100644 --- a/tests/acceptance/ExpenseCest.php +++ b/tests/acceptance/ExpenseCest.php @@ -49,7 +49,7 @@ class ExpenseCest // invoice expense $I->executeJS('submitAction(\'invoice\')'); $I->click('Save'); - $I->wait(2); + $I->wait(3); $I->see($clientEmail); $I->see($amount); } From 6cd4ff025ead38879899ca85027ca558015f4c62 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 3 May 2016 09:46:24 +0300 Subject: [PATCH 049/120] API optimizations --- app/Http/Controllers/BaseAPIController.php | 16 +++++++++++++--- app/Http/Controllers/ClientApiController.php | 1 - app/Http/Controllers/InvoiceApiController.php | 2 +- app/Http/Controllers/PaymentApiController.php | 2 +- app/Http/Controllers/TaskApiController.php | 1 - app/Http/Controllers/VendorApiController.php | 1 - app/Ninja/Transformers/ClientTransformer.php | 2 +- app/Ninja/Transformers/ExpenseTransformer.php | 10 ++++++++-- app/Ninja/Transformers/InvoiceTransformer.php | 11 +++++++++-- app/Ninja/Transformers/PaymentTransformer.php | 9 +++++---- 10 files changed, 38 insertions(+), 17 deletions(-) diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index 489053d055f0..07ed5bb42f03 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -3,6 +3,7 @@ use Session; use Utils; use Auth; +use Log; use Input; use Response; use Request; @@ -66,6 +67,10 @@ class BaseAPIController extends Controller } else { $this->manager->setSerializer(new ArraySerializer()); } + + if (Utils::isNinjaDev()) { + \DB::enableQueryLog(); + } } protected function handleAction($request) @@ -82,7 +87,8 @@ class BaseAPIController extends Controller protected function listResponse($query) { - //\DB::enableQueryLog(); + $query->with($this->getIncluded()); + if ($clientPublicId = Input::get('client_id')) { $filter = function($query) use ($clientPublicId) { $query->where('public_id', '=', $clientPublicId); @@ -103,7 +109,6 @@ class BaseAPIController extends Controller $data = $this->createCollection($query, $transformer, $this->entityType); - //return \DB::getQueryLog(); return $this->response($data); } @@ -113,7 +118,7 @@ class BaseAPIController extends Controller $transformer = new $transformerClass(Auth::user()->account, Input::get('serializer')); $data = $this->createItem($item, $transformer, $this->entityType); - + return $this->response($data); } @@ -146,6 +151,11 @@ class BaseAPIController extends Controller protected function response($response) { + if (Utils::isNinjaDev()) { + $count = count(\DB::getQueryLog()); + Log::info(Request::method() . ' - ' . Request::url() . ": $count queries"); + } + $index = Request::get('index') ?: 'data'; if ($index == 'none') { diff --git a/app/Http/Controllers/ClientApiController.php b/app/Http/Controllers/ClientApiController.php index ba6b56c952fa..557daa29fff6 100644 --- a/app/Http/Controllers/ClientApiController.php +++ b/app/Http/Controllers/ClientApiController.php @@ -44,7 +44,6 @@ class ClientApiController extends BaseAPIController public function index() { $clients = Client::scope() - ->with($this->getIncluded()) ->orderBy('created_at', 'desc') ->withTrashed(); diff --git a/app/Http/Controllers/InvoiceApiController.php b/app/Http/Controllers/InvoiceApiController.php index 32d38cc9316d..da8bc2b7a6cf 100644 --- a/app/Http/Controllers/InvoiceApiController.php +++ b/app/Http/Controllers/InvoiceApiController.php @@ -60,7 +60,7 @@ class InvoiceApiController extends BaseAPIController { $invoices = Invoice::scope() ->withTrashed() - ->with(array_merge(['invoice_items'], $this->getIncluded())) + ->with('invoice_items') ->orderBy('created_at', 'desc'); return $this->listResponse($invoices); diff --git a/app/Http/Controllers/PaymentApiController.php b/app/Http/Controllers/PaymentApiController.php index 5cea7f23641a..b41100acfad9 100644 --- a/app/Http/Controllers/PaymentApiController.php +++ b/app/Http/Controllers/PaymentApiController.php @@ -49,7 +49,7 @@ class PaymentApiController extends BaseAPIController { $payments = Payment::scope() ->withTrashed() - ->with(array_merge(['client.contacts', 'invitation', 'user', 'invoice'], $this->getIncluded())) + ->with(['client.contacts', 'invitation', 'user', 'invoice']) ->orderBy('created_at', 'desc'); return $this->listResponse($payments); diff --git a/app/Http/Controllers/TaskApiController.php b/app/Http/Controllers/TaskApiController.php index 5e5e05ef80e9..7945008bcc40 100644 --- a/app/Http/Controllers/TaskApiController.php +++ b/app/Http/Controllers/TaskApiController.php @@ -42,7 +42,6 @@ class TaskApiController extends BaseAPIController { $payments = Task::scope() ->withTrashed() - ->with($this->getIncluded()) ->orderBy('created_at', 'desc'); return $this->listResponse($payments); diff --git a/app/Http/Controllers/VendorApiController.php b/app/Http/Controllers/VendorApiController.php index d3cc27094760..2995e5c5e5f2 100644 --- a/app/Http/Controllers/VendorApiController.php +++ b/app/Http/Controllers/VendorApiController.php @@ -49,7 +49,6 @@ class VendorApiController extends BaseAPIController public function index() { $vendors = Vendor::scope() - ->with($this->getIncluded()) ->withTrashed() ->orderBy('created_at', 'desc'); diff --git a/app/Ninja/Transformers/ClientTransformer.php b/app/Ninja/Transformers/ClientTransformer.php index f7d786a2ee25..e9a63e07d693 100644 --- a/app/Ninja/Transformers/ClientTransformer.php +++ b/app/Ninja/Transformers/ClientTransformer.php @@ -58,7 +58,7 @@ class ClientTransformer extends EntityTransformer public function includeInvoices(Client $client) { - $transformer = new InvoiceTransformer($this->account, $this->serializer); + $transformer = new InvoiceTransformer($this->account, $this->serializer, $client); return $this->includeCollection($client->invoices, $transformer, ENTITY_INVOICE); } diff --git a/app/Ninja/Transformers/ExpenseTransformer.php b/app/Ninja/Transformers/ExpenseTransformer.php index a6d358d15766..8f2e02c0a88f 100644 --- a/app/Ninja/Transformers/ExpenseTransformer.php +++ b/app/Ninja/Transformers/ExpenseTransformer.php @@ -6,9 +6,15 @@ use League\Fractal; class ExpenseTransformer extends EntityTransformer { + public function __construct($account = null, $serializer = null, $client = null) + { + parent::__construct($account, $serializer); + + $this->client = $client; + } + public function transform(Expense $expense) { - return [ 'id' => (int) $expense->public_id, 'private_notes' => $expense->private_notes, @@ -25,7 +31,7 @@ class ExpenseTransformer extends EntityTransformer 'exchange_rate' => (float) $expense->exchange_rate, 'invoice_currency_id' => (int) $expense->invoice_currency_id, 'is_deleted' => (bool) $expense->is_deleted, - 'client_id' => isset($expense->client->public_id) ? (int) $expense->client->public_id : null, + 'client_id' => $this->client ? $this->client->public_id : (isset($expense->client->public_id) ? (int) $expense->client->public_id : null), 'invoice_id' => isset($expense->invoice->public_id) ? (int) $expense->invoice->public_id : null, 'vendor_id' => isset($expense->vendor->public_id) ? (int) $expense->vendor->public_id : null, ]; diff --git a/app/Ninja/Transformers/InvoiceTransformer.php b/app/Ninja/Transformers/InvoiceTransformer.php index 413f43ce1a10..51af3d029936 100644 --- a/app/Ninja/Transformers/InvoiceTransformer.php +++ b/app/Ninja/Transformers/InvoiceTransformer.php @@ -31,6 +31,13 @@ class InvoiceTransformer extends EntityTransformer 'expenses', ]; + public function __construct($account = null, $serializer = null, $client = null) + { + parent::__construct($account, $serializer); + + $this->client = $client; + } + public function includeInvoiceItems(Invoice $invoice) { $transformer = new InvoiceItemTransformer($this->account, $this->serializer); @@ -45,7 +52,7 @@ class InvoiceTransformer extends EntityTransformer public function includePayments(Invoice $invoice) { - $transformer = new PaymentTransformer($this->account, $this->serializer); + $transformer = new PaymentTransformer($this->account, $this->serializer, $invoice); return $this->includeCollection($invoice->payments, $transformer, ENTITY_PAYMENT); } @@ -68,7 +75,7 @@ class InvoiceTransformer extends EntityTransformer 'id' => (int) $invoice->public_id, 'amount' => (float) $invoice->amount, 'balance' => (float) $invoice->balance, - 'client_id' => (int) $invoice->client->public_id, + 'client_id' => (int) ($this->client ? $this->client->public_id : $invoice->client->public_id), 'invoice_status_id' => (int) $invoice->invoice_status_id, 'updated_at' => $this->getTimestamp($invoice->updated_at), 'archived_at' => $this->getTimestamp($invoice->deleted_at), diff --git a/app/Ninja/Transformers/PaymentTransformer.php b/app/Ninja/Transformers/PaymentTransformer.php index a1de09cd6ff4..8ea2dcbae051 100644 --- a/app/Ninja/Transformers/PaymentTransformer.php +++ b/app/Ninja/Transformers/PaymentTransformer.php @@ -26,10 +26,11 @@ class PaymentTransformer extends EntityTransformer ]; - public function __construct(Account $account) + public function __construct($account = null, $serializer = null, $invoice = null) { - parent::__construct($account); - + parent::__construct($account, $serializer); + + $this->invoice = $invoice; } public function includeInvoice(Payment $payment) @@ -57,7 +58,7 @@ class PaymentTransformer extends EntityTransformer 'archived_at' => $this->getTimestamp($payment->deleted_at), 'is_deleted' => (bool) $payment->is_deleted, 'payment_type_id' => (int) $payment->payment_type_id, - 'invoice_id' => (int) $payment->invoice->public_id, + 'invoice_id' => (int) ($this->invoice ? $this->invoice->public_id : $payment->invoice->public_id), ]; } } \ No newline at end of file From 0645cdddb15b8487c50432c447722bbe942823f6 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 3 May 2016 09:47:47 +0300 Subject: [PATCH 050/120] Fix for travis tests --- tests/acceptance/ExpenseCest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/acceptance/ExpenseCest.php b/tests/acceptance/ExpenseCest.php index 72d7666aab9d..6fb3db0ff192 100644 --- a/tests/acceptance/ExpenseCest.php +++ b/tests/acceptance/ExpenseCest.php @@ -48,8 +48,10 @@ class ExpenseCest // invoice expense $I->executeJS('submitAction(\'invoice\')'); + $I->wait(2); + $I->click('Save'); - $I->wait(3); + $I->wait(2); $I->see($clientEmail); $I->see($amount); } From be420ad1fc7cfadc62c9bfa8c66fa769103d81d0 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 3 May 2016 11:39:10 +0300 Subject: [PATCH 051/120] API optimizations --- app/Http/Controllers/BaseAPIController.php | 15 +++++++++------ app/Ninja/Transformers/EntityTransformer.php | 5 +++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index 07ed5bb42f03..a1983ed2b5f0 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -87,7 +87,12 @@ class BaseAPIController extends Controller protected function listResponse($query) { - $query->with($this->getIncluded()); + $transformerClass = EntityModel::getTransformerName($this->entityType); + $transformer = new $transformerClass(Auth::user()->account, Input::get('serializer')); + + $include = $transformer->getDefaultIncludes(); + $include = $this->getRequestIncludes($include); + $query->with($include); if ($clientPublicId = Input::get('client_id')) { $filter = function($query) use ($clientPublicId) { @@ -104,9 +109,6 @@ class BaseAPIController extends Controller } } - $transformerClass = EntityModel::getTransformerName($this->entityType); - $transformer = new $transformerClass(Auth::user()->account, Input::get('serializer')); - $data = $this->createCollection($query, $transformer, $this->entityType); return $this->response($data); @@ -154,6 +156,7 @@ class BaseAPIController extends Controller if (Utils::isNinjaDev()) { $count = count(\DB::getQueryLog()); Log::info(Request::method() . ' - ' . Request::url() . ": $count queries"); + //Log::info(print_r(\DB::getQueryLog(), true)); } $index = Request::get('index') ?: 'data'; @@ -188,9 +191,9 @@ class BaseAPIController extends Controller } - protected function getIncluded() + protected function getRequestIncludes($data) { - $data = ['user']; + $data[] = 'user'; $included = Request::get('include'); $included = explode(',', $included); diff --git a/app/Ninja/Transformers/EntityTransformer.php b/app/Ninja/Transformers/EntityTransformer.php index ea0dba263d97..af1304acd67f 100644 --- a/app/Ninja/Transformers/EntityTransformer.php +++ b/app/Ninja/Transformers/EntityTransformer.php @@ -37,4 +37,9 @@ class EntityTransformer extends TransformerAbstract { return $date ? $date->getTimestamp() : null; } + + public function getDefaultIncludes() + { + return $this->defaultIncludes; + } } From 871456d402bffb441b8a404a2003afb8d7394648 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 3 May 2016 11:40:17 +0300 Subject: [PATCH 052/120] Fix for travis test --- tests/acceptance/ExpenseCest.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/acceptance/ExpenseCest.php b/tests/acceptance/ExpenseCest.php index 6fb3db0ff192..e22bebac3b7f 100644 --- a/tests/acceptance/ExpenseCest.php +++ b/tests/acceptance/ExpenseCest.php @@ -48,10 +48,9 @@ class ExpenseCest // invoice expense $I->executeJS('submitAction(\'invoice\')'); - $I->wait(2); - + $I->wait(3); $I->click('Save'); - $I->wait(2); + $I->wait(3); $I->see($clientEmail); $I->see($amount); } From bf4c4d0ce556f504f49ca81cc1fcb63070b9d8e1 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 3 May 2016 11:53:00 +0300 Subject: [PATCH 053/120] Add user permissions to API delete --- app/Http/Controllers/BaseAPIController.php | 3 +++ app/Http/Controllers/ClientApiController.php | 17 +++++------------ app/Http/Controllers/InvoiceApiController.php | 13 ++++--------- app/Http/Controllers/PaymentApiController.php | 18 +++++++----------- 4 files changed, 19 insertions(+), 32 deletions(-) diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index a1983ed2b5f0..db99a3beaf1d 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -202,6 +202,9 @@ class BaseAPIController extends Controller if ($include == 'invoices') { $data[] = 'invoices.invoice_items'; $data[] = 'invoices.user'; + } elseif ($include == 'client') { + $data[] = 'client.contacts'; + $data[] = 'client.user'; } elseif ($include == 'clients') { $data[] = 'clients.contacts'; $data[] = 'clients.user'; diff --git a/app/Http/Controllers/ClientApiController.php b/app/Http/Controllers/ClientApiController.php index 557daa29fff6..dd82e9116131 100644 --- a/app/Http/Controllers/ClientApiController.php +++ b/app/Http/Controllers/ClientApiController.php @@ -143,20 +143,13 @@ class ClientApiController extends BaseAPIController * ) */ - public function destroy($publicId) + public function destroy(UpdateClientRequest $request) { - $client = Client::scope($publicId)->withTrashed()->first(); + $client = $request->entity(); + $this->clientRepo->delete($client); - $client = Client::scope($publicId) - ->with('country', 'contacts', 'industry', 'size', 'currency') - ->withTrashed() - ->first(); - - $transformer = new ClientTransformer(Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($client, $transformer, ENTITY_CLIENT); - - return $this->response($data); + return $this->itemResponse($client); } - + } \ No newline at end of file diff --git a/app/Http/Controllers/InvoiceApiController.php b/app/Http/Controllers/InvoiceApiController.php index da8bc2b7a6cf..1361aad9e9a3 100644 --- a/app/Http/Controllers/InvoiceApiController.php +++ b/app/Http/Controllers/InvoiceApiController.php @@ -349,18 +349,13 @@ class InvoiceApiController extends BaseAPIController * ) */ - public function destroy($publicId) + public function destroy(UpdateInvoiceAPIRequest $request) { - $data['public_id'] = $publicId; - $invoice = Invoice::scope($publicId)->firstOrFail(); - + $invoice = $request->entity(); + $this->invoiceRepo->delete($invoice); - $transformer = new InvoiceTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($invoice, $transformer, 'invoice'); - - return $this->response($data); - + return $this->itemResponse($invoice); } } diff --git a/app/Http/Controllers/PaymentApiController.php b/app/Http/Controllers/PaymentApiController.php index b41100acfad9..0dd8de50803b 100644 --- a/app/Http/Controllers/PaymentApiController.php +++ b/app/Http/Controllers/PaymentApiController.php @@ -49,7 +49,7 @@ class PaymentApiController extends BaseAPIController { $payments = Payment::scope() ->withTrashed() - ->with(['client.contacts', 'invitation', 'user', 'invoice']) + ->with(['invoice']) ->orderBy('created_at', 'desc'); return $this->listResponse($payments); @@ -145,17 +145,13 @@ class PaymentApiController extends BaseAPIController * ) */ - public function destroy($publicId) + public function destroy(UpdatePaymentRequest $request) { + $payment = $request->entity(); + + $this->clientRepo->delete($payment); - $payment = Payment::scope($publicId)->withTrashed()->first(); - $invoiceId = $payment->invoice->public_id; - - $this->paymentRepo->delete($payment); - - $transformer = new PaymentTransformer(\Auth::user()->account, Input::get('serializer')); - $data = $this->createItem($payment, $transformer, 'invoice'); - - return $this->response($data); + return $this->itemResponse($payment); } + } From 75ee124626bece7516de5a4bf952ae78713d7aaa Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 3 May 2016 14:05:35 +0300 Subject: [PATCH 054/120] Fix for TD Bank w/OFX --- app/Libraries/OFX.php | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/app/Libraries/OFX.php b/app/Libraries/OFX.php index 5563e917acf7..721c9f529f85 100644 --- a/app/Libraries/OFX.php +++ b/app/Libraries/OFX.php @@ -2,6 +2,8 @@ // https://github.com/denvertimothy/OFX +use Utils; +use Log; use SimpleXMLElement; class OFX @@ -21,13 +23,19 @@ class OFX $c = curl_init(); curl_setopt($c, CURLOPT_URL, $this->bank->url); curl_setopt($c, CURLOPT_POST, 1); - curl_setopt($c, CURLOPT_HTTPHEADER, array('Content-Type: application/x-ofx')); + // User-Agent: http://www.ofxhome.com/ofxforum/viewtopic.php?pid=108091#p108091 + curl_setopt($c, CURLOPT_HTTPHEADER, array('Content-Type: application/x-ofx', 'User-Agent: httpclient')); curl_setopt($c, CURLOPT_POSTFIELDS, $this->request); curl_setopt($c, CURLOPT_RETURNTRANSFER, 1); + $this->response = curl_exec($c); - //print_r($this->response); - //\Log::info(print_r($this->response, true)); + + if (Utils::isNinjaDev()) { + Log::info(print_r($this->response, true)); + } + curl_close($c); + $tmp = explode('', $this->response); $this->responseHeader = $tmp[0]; $this->responseBody = ''.$tmp[1]; @@ -35,14 +43,15 @@ class OFX public function xml() { $xml = $this->responseBody; - self::closeTags($xml); + $xml = self::closeTags($xml); $x = new SimpleXMLElement($xml); return $x; } - public static function closeTags(&$x) + public static function closeTags($x) { - $x = preg_replace('/(<([^<\/]+)>)(?!.*?<\/\2>)([^<]+)/', '\1\3', $x); + $x = preg_replace('/\s+/', '', $x); + return preg_replace('/(<([^<\/]+)>)(?!.*?<\/\2>)([^<]+)/', '\1\3', $x); } } @@ -224,3 +233,4 @@ class Account } } } + From 39141d469f5f69ec9eb1923e6a7968fe2430c346 Mon Sep 17 00:00:00 2001 From: Bartlomiej Szala Date: Tue, 3 May 2016 12:47:26 +0000 Subject: [PATCH 055/120] Add polish translations --- ...16_05_02_071859_add_polish_translation.php | 29 + resources/lang/pl/pagination.php | 20 + resources/lang/pl/passwords.php | 22 + resources/lang/pl/reminders.php | 24 + resources/lang/pl/texts.php | 1184 +++++++++++++++++ resources/lang/pl/validation.php | 106 ++ 6 files changed, 1385 insertions(+) create mode 100644 database/migrations/2016_05_02_071859_add_polish_translation.php create mode 100644 resources/lang/pl/pagination.php create mode 100644 resources/lang/pl/passwords.php create mode 100644 resources/lang/pl/reminders.php create mode 100644 resources/lang/pl/texts.php create mode 100644 resources/lang/pl/validation.php diff --git a/database/migrations/2016_05_02_071859_add_polish_translation.php b/database/migrations/2016_05_02_071859_add_polish_translation.php new file mode 100644 index 000000000000..b78969cfbbde --- /dev/null +++ b/database/migrations/2016_05_02_071859_add_polish_translation.php @@ -0,0 +1,29 @@ +insert(['name' => 'Polish', 'locale' => self::LANGUAGE_LOCALE]); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + $language = \App\Models\Language::whereLocale(self::LANGUAGE_LOCALE)->first(); + $language->delete(); + } +} diff --git a/resources/lang/pl/pagination.php b/resources/lang/pl/pagination.php new file mode 100644 index 000000000000..950c5e96cc41 --- /dev/null +++ b/resources/lang/pl/pagination.php @@ -0,0 +1,20 @@ + '« Poprzedni', + + 'next' => 'Następny »', + +); diff --git a/resources/lang/pl/passwords.php b/resources/lang/pl/passwords.php new file mode 100644 index 000000000000..28ccb957b349 --- /dev/null +++ b/resources/lang/pl/passwords.php @@ -0,0 +1,22 @@ + "Hasło musi mieć conajmniej sześć znaków i być takie samo jak potwierdzające.", + "user" => "Użytkownik o podanym adresie e-mail nie istnieje.", + "token" => "Wprowadzony token jest nieprawidłowy.", + "sent" => "Link do resetowania hasła został wysłany.", + "reset" => "Twoje hasło zostało zresetowane!", + +]; \ No newline at end of file diff --git a/resources/lang/pl/reminders.php b/resources/lang/pl/reminders.php new file mode 100644 index 000000000000..25824a7f285a --- /dev/null +++ b/resources/lang/pl/reminders.php @@ -0,0 +1,24 @@ + "Hasło musi mieć conajmniej sześć znaków i być takie samo jak potwierdzające.", + + "user" => "Użytkownik o podanym adresie e-mail nie istnieje.", + + "token" => "Wprowadzony token jest nieprawidłowy.", + + "sent" => "Przypomnienie hasła zostało wysłane!", + +); \ No newline at end of file diff --git a/resources/lang/pl/texts.php b/resources/lang/pl/texts.php new file mode 100644 index 000000000000..19b0253937db --- /dev/null +++ b/resources/lang/pl/texts.php @@ -0,0 +1,1184 @@ + 'Organizacja', + 'name' => 'Nazwa', + 'website' => 'Strona internetowa', + 'work_phone' => 'Telefon służbowy', + 'address' => 'Adres', + 'address1' => 'Ulica', + 'address2' => 'Nr', + 'city' => 'Miasto', + 'state' => 'Województwo', + 'postal_code' => 'Kod pocztowy', + 'country_id' => 'Kraj', + 'contacts' => 'Kontakty', + 'first_name' => 'Imię', + 'last_name' => 'Nazwisko', + 'phone' => 'Telefon', + 'email' => 'Email', + 'additional_info' => 'Dodatkowe informacje', + 'payment_terms' => 'Warunki płatnicze', + 'currency_id' => 'Waluta', + 'size_id' => 'Wielkość firmy', + 'industry_id' => 'Branża', + 'private_notes' => 'Prywatne notatki', + 'invoice' => 'Faktura', + 'client' => 'Klient', + 'invoice_date' => 'Data Faktury', + 'due_date' => 'Termin', + 'invoice_number' => 'Numer Faktury', + 'invoice_number_short' => 'Faktura #', + 'po_number' => 'PO Number', + 'po_number_short' => 'PO #', + 'frequency_id' => 'Jak często', + 'discount' => 'Przecena', + 'taxes' => 'Podatki', + 'tax' => 'Podatek', + 'item' => 'Pozycja', + 'description' => 'Opis', + 'unit_cost' => 'Cena jednostkowa', + 'quantity' => 'Ilość', + 'line_total' => 'Wartość całkowita', + 'subtotal' => 'Suma częściowa', + 'paid_to_date' => 'Wypłacono do tej pory', + 'balance_due' => 'Balance Due', + 'invoice_design_id' => 'Szablon', + 'terms' => 'Warunki', + 'your_invoice' => 'Twoja faktura', + 'remove_contact' => 'Usuń kontakt', + 'add_contact' => 'Dodaj kontakt', + 'create_new_client' => 'Dodaj nowego klienta', + 'edit_client_details' => 'Edytuj dane klienta', + 'enable' => 'Aktywuj', + 'learn_more' => 'Więcej informacji', + 'manage_rates' => 'Zarządzaj stawkami', + 'note_to_client' => 'Informacja dla klienta', + 'invoice_terms' => 'Warunki do faktury', + 'save_as_default_terms' => 'Zapisz jako domyślne warunki', + 'download_pdf' => 'Pobierz PDF', + 'pay_now' => 'Zapłać teraz', + 'save_invoice' => 'Zapisz Fakturę', + 'clone_invoice' => 'Skopiuj Fakturę', + 'archive_invoice' => 'Zarchiwizuj Fakturę', + 'delete_invoice' => 'Usuń Fakturę', + 'email_invoice' => 'Wyślij Fakturę', + 'enter_payment' => 'Wprowadź Płatność', + 'tax_rates' => 'Stawki podatkowe', + 'rate' => 'Stawka', + 'settings' => 'Ustawienia', + 'enable_invoice_tax' => 'Aktywuj możliwość ustawienia podatku do faktury', + 'enable_line_item_tax' => 'Aktywuj możliwość ustawienia podatku do pozycji na fakturze', + 'dashboard' => 'Pulpit', + 'clients' => 'Klienci', + 'invoices' => 'Faktury', + 'payments' => 'Płatności', + 'credits' => 'Kredyty', + 'history' => 'Historia', + 'search' => 'Szukaj', + 'sign_up' => 'Zapisz się', + 'guest' => 'Gość', + 'company_details' => 'Dane Firmy', + 'online_payments' => 'Płatności Online', + 'notifications' => 'Powiadomienia E-mail', + 'import_export' => 'Import | Export', + 'done' => 'Gotowe', + 'save' => 'Zapisz', + 'create' => 'Dodaj', + 'upload' => 'Prześlij', + 'import' => 'Import', + 'download' => 'Pobierz', + 'cancel' => 'Anuluj', + 'close' => 'Zamknij', + 'provide_email' => 'Wprowadź poprawny e-mail.', + 'powered_by' => 'Oparte na', + 'no_items' => 'Brak pozycji', + 'recurring_invoices' => 'Faktury okresowe', + 'recurring_help' => '

    Wysyła automatycznie te same faktury klientom tygodniowo, miesięcznie, kwartałowo lub rocznie.

    +

    Użyj :MONTH, :QUARTER lub :YEAR dla dynamicznych dat. Podstawowa arytmetyka działa również, np: :MONTH-1.

    +

    Przykłady dynamicznych zmiennych na fakturze:

    +
      +
    • "Gym membership for the month of :MONTH" => "Gym membership for the month of July"
    • +
    • ":YEAR+1 yearly subscription" => "2015 Yearly Subscription"
    • +
    • "Retainer payment for :QUARTER+1" => "Retainer payment for Q2"
    • +
    ', + 'in_total_revenue' => 'całkowity przychód', + 'billed_client' => 'Obciążony klient', + 'billed_clients' => 'Obciążeni klienci', + 'active_client' => 'Aktywny klient', + 'active_clients' => 'Aktywni klienci', + 'invoices_past_due' => 'Opóźnione faktury', + 'upcoming_invoices' => 'Nadchodzące Faktury', + 'average_invoice' => 'Średnia wartość Faktury', + 'archive' => 'Archiwum', + 'delete' => 'Usuń', + 'archive_client' => 'Zarchiwizuj klienta', + 'delete_client' => 'Usuń klienta', + 'archive_payment' => 'Zarchiwizuj płatność', + 'delete_payment' => 'Usuń płatność', + 'archive_credit' => 'Zarchiwizuj kredyt', + 'delete_credit' => 'Usuń kredyt', + 'show_archived_deleted' => 'Pokaż zarchiwizowane/usunięte', + 'filter' => 'Filtruj', + 'new_client' => 'Nowy klient', + 'new_invoice' => 'Nowa Faktura', + 'new_payment' => 'Nowa Płatność', + 'new_credit' => 'Nowy Kredyt', + 'contact' => 'Kontakt', + 'date_created' => 'Data utworzenia', + 'last_login' => 'Ostatnie logowanie', + 'balance' => 'Saldo', + 'action' => 'Akcja', + 'status' => 'Status', + 'invoice_total' => 'Faktura ogółem', + 'frequency' => 'Częstotliwość', + 'start_date' => 'Początkowa data', + 'end_date' => 'Końcowa data', + 'transaction_reference' => 'Referencja transakcji', + 'method' => 'Metoda', + 'payment_amount' => 'Kwota płatności', + 'payment_date' => 'Data płatności', + 'credit_amount' => 'Kwota kredytu', + 'credit_balance' => 'Saldo kredytowe', + 'credit_date' => 'Data kredytu', + 'empty_table' => 'Brak danych w tabeli', + 'select' => 'Wybierz', + 'edit_client' => 'Edytuj klienta', + 'edit_invoice' => 'Edytuj fakturę', + 'create_invoice' => 'Utwórz Fakturę', + 'enter_credit' => 'Wprowadź kredyt', + 'last_logged_in' => 'Ostatnie logowanie w', + 'details' => 'Szczegóły', + 'standing' => 'Standing', + 'credit' => 'Kredyt', + 'activity' => 'Czynność', + 'date' => 'Data', + 'message' => 'Wiadomość', + 'adjustment' => 'Dostosowanie', + 'are_you_sure' => 'Jesteś pewny?', + 'payment_type_id' => 'Typ płatności', + 'amount' => 'Kwota', + 'work_email' => 'Email', + 'language_id' => 'Język', + 'timezone_id' => 'Strefa czasowa', + 'date_format_id' => 'Format daty', + 'datetime_format_id' => 'Format Data/Godzina', + 'users' => 'Użytkownicy', + 'localization' => 'Lokalizacja', + 'remove_logo' => 'Usuń logo', + 'logo_help' => 'Obsługiwane: JPEG, GIF i PNG', + 'payment_gateway' => 'Bramka płatnicza', + 'gateway_id' => 'Bramka płatnicza', + 'email_notifications' => 'Email powiadomienia', + 'email_sent' => 'Powiadom mnie kiedy faktura jest wysłana', + 'email_viewed' => 'Powiadom mnie kiedy faktura jest otworzona', + 'email_paid' => 'Powiadom mnie kiedy faktura jest zapłacona', + 'site_updates' => 'Aktualizacje strony', + 'custom_messages' => 'Niestandardowe komunikaty', + 'default_email_footer' => 'Ustaw domyślny podpis email', + 'select_file' => 'Wybierz plik', + 'first_row_headers' => 'Użyj pierwszego wiersza jako nagłówek', + 'column' => 'Kolumna', + 'sample' => 'Przykład', + 'import_to' => 'Zaimportuj do', + 'client_will_create' => 'klient będzie utworzony', + 'clients_will_create' => 'klienci będą utworzeni', + 'email_settings' => 'Ustawienia e-mail', + 'client_view_styling' => 'Client View Styling', + 'pdf_email_attachment' => 'Attach PDFs', + 'custom_css' => 'Custom CSS', + 'import_clients' => 'Import Client Data', + 'csv_file' => 'CSV file', + 'export_clients' => 'Export Client Data', + 'created_client' => 'Successfully created client', + 'created_clients' => 'Successfully created :count client(s)', + 'updated_settings' => 'Successfully updated settings', + 'removed_logo' => 'Successfully removed logo', + 'sent_message' => 'Successfully sent message', + 'invoice_error' => 'Please make sure to select a client and correct any errors', + 'limit_clients' => 'Sorry, this will exceed the limit of :count clients', + 'payment_error' => 'There was an error processing your payment. Please try again later.', + 'registration_required' => 'Please sign up to email an invoice', + 'confirmation_required' => 'Please confirm your email address', + 'updated_client' => 'Successfully updated client', + 'created_client' => 'Successfully created client', + 'archived_client' => 'Successfully archived client', + 'archived_clients' => 'Successfully archived :count clients', + 'deleted_client' => 'Successfully deleted client', + 'deleted_clients' => 'Successfully deleted :count clients', + 'updated_invoice' => 'Successfully updated invoice', + 'created_invoice' => 'Successfully created invoice', + 'cloned_invoice' => 'Successfully cloned invoice', + 'emailed_invoice' => 'Successfully emailed invoice', + 'and_created_client' => 'and created client', + 'archived_invoice' => 'Successfully archived invoice', + 'archived_invoices' => 'Successfully archived :count invoices', + 'deleted_invoice' => 'Successfully deleted invoice', + 'deleted_invoices' => 'Successfully deleted :count invoices', + 'created_payment' => 'Successfully created payment', + 'created_payments' => 'Successfully created :count payment(s)', + 'archived_payment' => 'Successfully archived payment', + 'archived_payments' => 'Successfully archived :count payments', + 'deleted_payment' => 'Successfully deleted payment', + 'deleted_payments' => 'Successfully deleted :count payments', + 'applied_payment' => 'Successfully applied payment', + 'created_credit' => 'Successfully created credit', + 'archived_credit' => 'Successfully archived credit', + 'archived_credits' => 'Successfully archived :count credits', + 'deleted_credit' => 'Successfully deleted credit', + 'deleted_credits' => 'Successfully deleted :count credits', + 'imported_file' => 'Successfully imported file', + 'updated_vendor' => 'Successfully updated vendor', + 'created_vendor' => 'Successfully created vendor', + 'archived_vendor' => 'Successfully archived vendor', + 'archived_vendors' => 'Successfully archived :count vendors', + 'deleted_vendor' => 'Successfully deleted vendor', + 'deleted_vendors' => 'Successfully deleted :count vendors', + 'confirmation_subject' => 'Invoice Ninja Account Confirmation', + 'confirmation_header' => 'Account Confirmation', + 'confirmation_message' => 'Please access the link below to confirm your account.', + 'invoice_subject' => 'New invoice :invoice from :account', + 'invoice_message' => 'To view your invoice for :amount, click the link below.', + 'payment_subject' => 'Payment Received', + 'payment_message' => 'Thank you for your payment of :amount.', + 'email_salutation' => 'Drogi :name,', + 'email_signature' => 'Pozdrowienia,', + 'email_from' => 'Zespół The Invoice Ninja', + 'invoice_link_message' => 'To view the invoice click the link below:', + 'notification_invoice_paid_subject' => 'Invoice :invoice was paid by :client', + 'notification_invoice_sent_subject' => 'Invoice :invoice was sent to :client', + 'notification_invoice_viewed_subject' => 'Invoice :invoice was viewed by :client', + 'notification_invoice_paid' => 'A payment of :amount was made by client :client towards Invoice :invoice.', + 'notification_invoice_sent' => 'The following client :client was emailed Invoice :invoice for :amount.', + 'notification_invoice_viewed' => 'The following client :client viewed Invoice :invoice for :amount.', + 'reset_password' => 'You can reset your account password by clicking the following button:', + 'secure_payment' => 'Bezpieczna płatność', + 'card_number' => 'Numer karty', + 'expiration_month' => 'Expiration Month', + 'expiration_year' => 'Expiration Year', + 'cvv' => 'CVV', + 'logout' => 'Wyloguj się', + 'sign_up_to_save' => 'Sign up to save your work', + 'agree_to_terms' => 'I agree to the Invoice Ninja :terms', + 'terms_of_service' => 'Terms of Service', + 'email_taken' => 'The email address is already registered', + 'working' => 'Working', + 'success' => 'Success', + 'success_message' => 'You have successfully registered! Please visit the link in the account confirmation email to verify your email address.', + 'erase_data' => 'This will permanently erase your data.', + 'password' => 'Hasło', + 'pro_plan_product' => 'Pro Plan', + 'pro_plan_success' => 'Thanks for choosing Invoice Ninja\'s Pro plan!

     
    + Next Steps

    A payable invoice has been sent to the email + address associated with your account. To unlock all of the awesome + Pro features, please follow the instructions on the invoice to pay + for a year of Pro-level invoicing.

    + Can\'t find the invoice? Need further assistance? We\'re happy to help + -- email us at contact@invoiceninja.com', + 'unsaved_changes' => 'You have unsaved changes', + 'custom_fields' => 'Custom Fields', + 'company_fields' => 'Company Fields', + 'client_fields' => 'Client Fields', + 'field_label' => 'Field Label', + 'field_value' => 'Field Value', + 'edit' => 'Edytuj', + 'set_name' => 'Set your company name', + 'view_as_recipient' => 'View as recipient', + 'product_library' => 'Product Library', + 'product' => 'Produkt', + 'products' => 'Produkty', + 'fill_products' => 'Auto-fill products', + 'fill_products_help' => 'Selecting a product will automatically fill in the description and cost', + 'update_products' => 'Auto-update products', + 'update_products_help' => 'Updating an invoice will automatically update the product library', + 'create_product' => 'Add Product', + 'edit_product' => 'Edit Product', + 'archive_product' => 'Archive Product', + 'updated_product' => 'Successfully updated product', + 'created_product' => 'Successfully created product', + 'archived_product' => 'Successfully archived product', + 'pro_plan_custom_fields' => ':link to enable custom fields by joining the Pro Plan', + 'advanced_settings' => 'Advanced Settings', + 'pro_plan_advanced_settings' => ':link to enable the advanced settings by joining the Pro Plan', + 'invoice_design' => 'Invoice Design', + 'specify_colors' => 'Specify colors', + 'specify_colors_label' => 'Select the colors used in the invoice', + 'chart_builder' => 'Chart Builder', + 'ninja_email_footer' => 'Use :site to invoice your clients and get paid online for free!', + 'go_pro' => 'Go Pro', + 'quote' => 'Oferta', + 'quotes' => 'Oferty', + 'quote_number' => 'Numer oferty', + 'quote_number_short' => 'Oferta #', + 'quote_date' => 'Data oferty', + 'quote_total' => 'Suma oferty', + 'your_quote' => 'Twoja oferta', + 'total' => 'Suma', + 'clone' => 'Clone', + 'new_quote' => 'Nowa oferta', + 'create_quote' => 'Stwórz ofertę', + 'edit_quote' => 'Edytuj ofertę', + 'archive_quote' => 'Archiwizuj ofertę', + 'delete_quote' => 'Usuń ofertę', + 'save_quote' => 'Zapisz ofertę', + 'email_quote' => 'Wyślij ofertę', + 'clone_quote' => 'Clone Quote', + 'convert_to_invoice' => 'Konwertuj do faktury', + 'view_invoice' => 'Zobacz fakturę', + 'view_client' => 'Zobacz klienta', + 'view_quote' => 'Zobacz ofertę', + 'updated_quote' => 'Successfully updated quote', + 'created_quote' => 'Successfully created quote', + 'cloned_quote' => 'Successfully cloned quote', + 'emailed_quote' => 'Successfully emailed quote', + 'archived_quote' => 'Successfully archived quote', + 'archived_quotes' => 'Successfully archived :count quotes', + 'deleted_quote' => 'Successfully deleted quote', + 'deleted_quotes' => 'Successfully deleted :count quotes', + 'converted_to_invoice' => 'Successfully converted quote to invoice', + 'quote_subject' => 'New quote $quote from :account', + 'quote_message' => 'To view your quote for :amount, click the link below.', + 'quote_link_message' => 'To view your client quote click the link below:', + 'notification_quote_sent_subject' => 'Quote :invoice was sent to :client', + 'notification_quote_viewed_subject' => 'Quote :invoice was viewed by :client', + 'notification_quote_sent' => 'The following client :client was emailed Quote :invoice for :amount.', + 'notification_quote_viewed' => 'The following client :client viewed Quote :invoice for :amount.', + 'session_expired' => 'Your session has expired.', + 'invoice_fields' => 'Invoice Fields', + 'invoice_options' => 'Invoice Options', + 'hide_quantity' => 'Hide Quantity', + 'hide_quantity_help' => 'If your line items quantities are always 1, then you can declutter invoices by no longer displaying this field.', + 'hide_paid_to_date' => 'Hide Paid to Date', + 'hide_paid_to_date_help' => 'Only display the "Paid to Date" area on your invoices once a payment has been received.', + 'charge_taxes' => 'Charge taxes', + 'user_management' => 'User Management', + 'add_user' => 'Add User', + 'send_invite' => 'Send invitation', + 'sent_invite' => 'Successfully sent invitation', + 'updated_user' => 'Successfully updated user', + 'invitation_message' => 'You\'ve been invited by :invitor. ', + 'register_to_add_user' => 'Please sign up to add a user', + 'user_state' => 'State', + 'edit_user' => 'Edit User', + 'delete_user' => 'Delete User', + 'active' => 'Active', + 'pending' => 'Pending', + 'deleted_user' => 'Successfully deleted user', + 'confirm_email_invoice' => 'Are you sure you want to email this invoice?', + 'confirm_email_quote' => 'Are you sure you want to email this quote?', + 'confirm_recurring_email_invoice' => 'Are you sure you want this invoice emailed?', + 'cancel_account' => 'Usuń konto', + 'cancel_account_message' => 'Warning: This will permanently erase all of your data, there is no undo.', + 'go_back' => 'Go Back', + 'data_visualizations' => 'Data Visualizations', + 'sample_data' => 'Sample data shown', + 'hide' => 'Hide', + 'new_version_available' => 'A new version of :releases_link is available. You\'re running v:user_version, the latest is v:latest_version', + 'invoice_settings' => 'Invoice Settings', + 'invoice_number_prefix' => 'Invoice Number Prefix', + 'invoice_number_counter' => 'Invoice Number Counter', + 'quote_number_prefix' => 'Quote Number Prefix', + 'quote_number_counter' => 'Quote Number Counter', + 'share_invoice_counter' => 'Share invoice counter', + 'invoice_issued_to' => 'Invoice issued to', + 'invalid_counter' => 'To prevent a possible conflict please set either an invoice or quote number prefix', + 'mark_sent' => 'Mark Sent', + 'gateway_help_1' => ':link to sign up for Authorize.net.', + 'gateway_help_2' => ':link to sign up for Authorize.net.', + 'gateway_help_17' => ':link to get your PayPal API signature.', + 'gateway_help_27' => ':link to sign up for TwoCheckout.', + 'more_designs' => 'More designs', + 'more_designs_title' => 'Additional Invoice Designs', + 'more_designs_cloud_header' => 'Go Pro for more invoice designs', + 'more_designs_cloud_text' => '', + 'more_designs_self_host_text' => '', + 'buy' => 'Buy', + 'bought_designs' => 'Successfully added additional invoice designs', + 'sent' => 'sent', + 'vat_number' => 'VAT Number', + 'timesheets' => 'Timesheets', + 'payment_title' => 'Enter Your Billing Address and Credit Card information', + 'payment_cvv' => '*This is the 3-4 digit number onthe back of your card', + 'payment_footer1' => '*Billing address must match address associated with credit card.', + 'payment_footer2' => '*Please click "PAY NOW" only once - transaction may take up to 1 minute to process.', + 'id_number' => 'ID Number', + 'white_label_link' => 'White label', + 'white_label_header' => 'White Label', + 'bought_white_label' => 'Successfully enabled white label license', + 'white_labeled' => 'White labeled', + 'restore' => 'Przywróć', + 'restore_invoice' => 'Przywróć fakturę', + 'restore_quote' => 'Przywróć ofertę', + 'restore_client' => 'Przywróć klienta', + 'restore_credit' => 'Przywróć kredyt', + 'restore_payment' => 'Przywróć płatność', + 'restored_invoice' => 'Faktura została przywrócona', + 'restored_quote' => 'Oferta została przywrócona', + 'restored_client' => 'Klient został przywrócony', + 'restored_payment' => 'Płatność została przywrócona', + 'restored_credit' => 'Kredyt został przywrócony', + 'reason_for_canceling' => 'Help us improve our site by telling us why you\'re leaving.', + 'discount_percent' => 'Procent', + 'discount_amount' => 'Kwota', + 'invoice_history' => 'Historia faktury', + 'quote_history' => 'Historia oferty', + 'current_version' => 'Aktualna wersja', + 'select_version' => 'Wybierz wersję', + 'view_history' => 'Zobacz historię', + 'edit_payment' => 'Edytuj płatność', + 'updated_payment' => 'Successfully updated payment', + 'deleted' => 'Usunięte', + 'restore_user' => 'Przywróć użytkownika', + 'restored_user' => 'Użytkownik został przywrócony', + 'show_deleted_users' => 'Pokaż usuniętych użytkowników', + 'email_templates' => 'Szablony e-mail', + 'invoice_email' => 'Email faktury', + 'payment_email' => 'Email płatności', + 'quote_email' => 'Email oferty', + 'reset_all' => 'Resetuj wszystko', + 'approve' => 'Zatwierdź', + 'token_billing_type_id' => 'Token Billing', + 'token_billing_help' => 'Enables you to store credit cards with your gateway, and charge them at a later date.', + 'token_billing_1' => 'Wyłączone', + 'token_billing_2' => 'Opt-in - checkbox is shown but not selected', + 'token_billing_3' => 'Opt-out - checkbox is shown and selected', + 'token_billing_4' => 'Zawsze', + 'token_billing_checkbox' => 'Store credit card details', + 'view_in_stripe' => 'View in Stripe', + 'use_card_on_file' => 'Use card on file', + 'edit_payment_details' => 'Edit payment details', + 'token_billing' => 'Save card details', + 'token_billing_secure' => 'The data is stored securely by :stripe_link', + 'support' => 'Support', + 'contact_information' => 'Contact Information', + '256_encryption' => '256-Bit Encryption', + 'amount_due' => 'Amount due', + 'billing_address' => 'Adres billingowy (rozliczeniowy)', + 'billing_method' => 'Billing Method', + 'order_overview' => 'Order overview', + 'match_address' => '*Address must match address associated with credit card.', + 'click_once' => '*Please click "PAY NOW" only once - transaction may take up to 1 minute to process.', + 'invoice_footer' => 'Stopka faktury', + 'save_as_default_footer' => 'Save as default footer', + 'token_management' => 'Token Management', + 'tokens' => 'Tokens', + 'add_token' => 'Add Token', + 'show_deleted_tokens' => 'Show deleted tokens', + 'deleted_token' => 'Successfully deleted token', + 'created_token' => 'Successfully created token', + 'updated_token' => 'Successfully updated token', + 'edit_token' => 'Edit Token', + 'delete_token' => 'Delete Token', + 'token' => 'Token', + 'add_gateway' => 'Add Gateway', + 'delete_gateway' => 'Delete Gateway', + 'edit_gateway' => 'Edit Gateway', + 'updated_gateway' => 'Successfully updated gateway', + 'created_gateway' => 'Successfully created gateway', + 'deleted_gateway' => 'Successfully deleted gateway', + 'pay_with_paypal' => 'PayPal', + 'pay_with_card' => 'Credit Card', + 'change_password' => 'Zmień hasło', + 'current_password' => 'Aktualne hasło', + 'new_password' => 'Nowe hasło', + 'confirm_password' => 'Potwierdź hasło', + 'password_error_incorrect' => 'The current password is incorrect.', + 'password_error_invalid' => 'The new password is invalid.', + 'updated_password' => 'Successfully updated password', + 'api_tokens' => 'API Tokens', + 'users_and_tokens' => 'Users & Tokens', + 'account_login' => 'Account Login', + 'recover_password' => 'Recover your password', + 'forgot_password' => 'Forgot your password?', + 'email_address' => 'Email address', + 'lets_go' => 'Let\'s go', + 'password_recovery' => 'Password Recovery', + 'send_email' => 'Wyślij email', + 'set_password' => 'Ustaw hasło', + 'converted' => 'Converted', + 'email_approved' => 'Email me when a quote is approved', + 'notification_quote_approved_subject' => 'Quote :invoice was approved by :client', + 'notification_quote_approved' => 'The following client :client approved Quote :invoice for :amount.', + 'resend_confirmation' => 'Resend confirmation email', + 'confirmation_resent' => 'The confirmation email was resent', + 'gateway_help_42' => ':link to sign up for BitPay.
    Note: use a Legacy API Key, not an API token.', + 'payment_type_credit_card' => 'Karta kredytowa', + 'payment_type_paypal' => 'PayPal', + 'payment_type_bitcoin' => 'Bitcoin', + 'knowledge_base' => 'Baza wiedzy', + 'partial' => 'Partial', + 'partial_remaining' => ':partial of :balance', + 'more_fields' => 'More Fields', + 'less_fields' => 'Less Fields', + 'client_name' => 'Nazwa klienta', + 'pdf_settings' => 'Ustawienia PDF', + 'product_settings' => 'Ustawienia produktu', + 'auto_wrap' => 'Auto Line Wrap', + 'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.', + 'view_documentation' => 'View Documentation', + 'app_title' => 'Free Open-Source Online Invoicing', + 'app_description' => 'Invoice Ninja is a free, open-source solution for invoicing and billing customers. With Invoice Ninja, you can easily build and send beautiful invoices from any device that has access to the web. Your clients can print your invoices, download them as pdf files, and even pay you online from within the system.', + 'rows' => 'wierszy', + 'www' => 'www', + 'logo' => 'Logo', + 'subdomain' => 'Subdomain', + 'provide_name_or_email' => 'Proszę podać imię i nazwisko lub adres e-mail', + 'charts_and_reports' => 'Raporty i wykresy', + 'chart' => 'Wykres', + 'report' => 'Raport', + 'group_by' => 'Grupuj według', + 'paid' => 'Zapłacone', + 'enable_report' => 'Raport', + 'enable_chart' => 'Wykres', + 'totals' => 'Suma', + 'run' => 'Run', + 'export' => 'Export', + 'documentation' => 'Documentation', + 'zapier' => 'Zapier', + 'recurring' => 'Okresowe', + 'last_invoice_sent' => 'Last invoice sent :date', + 'processed_updates' => 'Pomyślnie zakończona aktualizacja', + 'tasks' => 'Zadania', + 'new_task' => 'Nowe zadanie', + 'start_time' => 'Czas rozpoczęcia', + 'created_task' => 'Pomyślnie utworzono zadanie', + 'updated_task' => 'Pomyślnie zaktualizowano zadanie', + 'edit_task' => 'Edytuj zadanie', + 'archive_task' => 'Archiwizuj zadanie', + 'restore_task' => 'Przywróć zadanie', + 'delete_task' => 'Usuń zadanie', + 'stop_task' => 'Zatrzymaj zadanie', + 'time' => 'Czas', + 'start' => 'Rozpocznij', + 'stop' => 'Zatrzymaj', + 'now' => 'Teraz', + 'timer' => 'Czasomierz', + 'manual' => 'Manualnie', + 'date_and_time' => 'Data i czas', + 'second' => 'sekunda', + 'seconds' => 'sekund', + 'minute' => 'minuta', + 'minutes' => 'minut', + 'hour' => 'godzina', + 'hours' => 'godzin', + 'task_details' => 'Szczegóły zadania', + 'duration' => 'Czas trwania', + 'end_time' => 'Czas zakończenia', + 'end' => 'Koniec', + 'invoiced' => 'Invoiced', + 'logged' => 'Zalogowany', + 'running' => 'Running', + 'task_error_multiple_clients' => 'The tasks can\'t belong to different clients', + 'task_error_running' => 'Please stop running tasks first', + 'task_error_invoiced' => 'Tasks have already been invoiced', + 'restored_task' => 'Successfully restored task', + 'archived_task' => 'Successfully archived task', + 'archived_tasks' => 'Successfully archived :count tasks', + 'deleted_task' => 'Successfully deleted task', + 'deleted_tasks' => 'Successfully deleted :count tasks', + 'create_task' => 'Create Task', + 'stopped_task' => 'Successfully stopped task', + 'invoice_task' => 'Invoice Task', + 'invoice_labels' => 'Invoice Labels', + 'prefix' => 'Prefix', + 'counter' => 'Counter', + 'payment_type_dwolla' => 'Dwolla', + 'gateway_help_43' => ':link to sign up for Dwolla', + 'partial_value' => 'Must be greater than zero and less than the total', + 'more_actions' => 'More Actions', + 'pro_plan_title' => 'NINJA PRO', + 'pro_plan_call_to_action' => 'Upgrade Now!', + 'pro_plan_feature1' => 'Create Unlimited Clients', + 'pro_plan_feature2' => 'Access to 10 Beautiful Invoice Designs', + 'pro_plan_feature3' => 'Custom URLs - "YourBrand.InvoiceNinja.com"', + 'pro_plan_feature4' => 'Remove "Created by Invoice Ninja"', + 'pro_plan_feature5' => 'Multi-user Access & Activity Tracking', + 'pro_plan_feature6' => 'Create Quotes & Pro-forma Invoices', + 'pro_plan_feature7' => 'Customize Invoice Field Titles & Numbering', + 'pro_plan_feature8' => 'Option to Attach PDFs to Client Emails', + 'resume' => 'Resume', + 'break_duration' => 'Break', + 'edit_details' => 'Edit Details', + 'work' => 'Work', + 'timezone_unset' => 'Please :link to set your timezone', + 'click_here' => 'click here', + 'email_receipt' => 'Wyślij potwierdzenie zapłaty do klienta', + 'created_payment_emailed_client' => 'Successfully created payment and emailed client', + 'add_company' => 'Add Company', + 'untitled' => 'Untitled', + 'new_company' => 'New Company', + 'associated_accounts' => 'Successfully linked accounts', + 'unlinked_account' => 'Successfully unlinked accounts', + 'login' => 'Login', + 'or' => 'or', + 'email_error' => 'There was a problem sending the email', + 'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.', + 'payment_terms_help' => 'Sets the default invoice due date', + 'unlink_account' => 'Unlink Account', + 'unlink' => 'Unlink', + 'show_address' => 'Show Address', + 'show_address_help' => 'Require client to provide their billing address', + 'update_address' => 'Update Address', + 'update_address_help' => 'Update client\'s address with provided details', + 'times' => 'krotnie', + 'set_now' => 'Ustaw na teraz', + 'dark_mode' => 'Tryb ciemny', + 'dark_mode_help' => 'Show white text on black background', + 'add_to_invoice' => 'Add to invoice :invoice', + 'create_new_invoice' => 'Create new invoice', + 'task_errors' => 'Please correct any overlapping times', + 'from' => 'From', + 'to' => 'To', + 'font_size' => 'Font Size', + 'primary_color' => 'Primary Color', + 'secondary_color' => 'Secondary Color', + 'customize_design' => 'Customize Design', + 'content' => 'Content', + 'styles' => 'Styles', + 'defaults' => 'Defaults', + 'margins' => 'Margins', + 'header' => 'Header', + 'footer' => 'Footer', + 'custom' => 'Custom', + 'invoice_to' => 'Invoice to', + 'invoice_no' => 'Invoice No.', + 'recent_payments' => 'Ostatnie płatności', + 'outstanding' => 'Zaległe', + 'manage_companies' => 'Manage Companies', + 'total_revenue' => 'Całkowity dochód', + 'current_user' => 'Aktualny użytkownik', + 'new_recurring_invoice' => 'Nowa faktura okresowa', + 'recurring_invoice' => 'Okresowa faktura', + 'recurring_too_soon' => 'It\'s too soon to create the next recurring invoice, it\'s scheduled for :date', + 'created_by_invoice' => 'Utworzona przez :invoice', + 'primary_user' => 'Główny Użytkownik', + 'help' => 'Pomoc', + 'customize_help' => '

    We use pdfmake to define the invoice designs declaratively. The pdfmake playground provide\'s a great way to see the library in action.

    +

    To access a child property using dot notation. For example to show the client name you could use $client.name.

    +

    If you need help figuring something out post a question to our support forum.

    ', + 'invoice_due_date' => 'Termin', + 'quote_due_date' => 'Valid Until', + 'valid_until' => 'Valid Until', + 'reset_terms' => 'Resetuj warunki', + 'reset_footer' => 'Resetuj stópkę', + 'invoices_sent' => ':count faktura wysłana|:count faktury wysłane', + 'status_draft' => 'Wersja robocza', + 'status_sent' => 'Wysłane', + 'status_viewed' => 'Przeglądnięte', + 'status_partial' => 'Częściowo', + 'status_paid' => 'Zapłacone', + 'show_line_item_tax' => 'Wyświetl podatki pozycji w tej samej linii', + 'iframe_url' => 'Website', + 'iframe_url_help1' => 'Copy the following code to a page on your site.', + 'iframe_url_help2' => 'You can test the feature by clicking \'View as recipient\' for an invoice.', + 'auto_bill' => 'Automatyczny Rachunek', + 'military_time' => '24 godzinny czas', + 'last_sent' => 'Ostatnio wysłany', + 'reminder_emails' => 'Reminder Emails', + 'templates_and_reminders' => 'Szablony i przypomnienia', + 'subject' => 'Temat', + 'body' => 'Treść', + 'first_reminder' => 'Pierwsze przypomnienie', + 'second_reminder' => 'Drugie przypomnienie', + 'third_reminder' => 'Trzecie przypomnienie', + 'num_days_reminder' => 'Dni po terminie', + 'reminder_subject' => 'Reminder: Invoice :invoice from :account', + 'reset' => 'Reset', + 'invoice_not_found' => 'The requested invoice is not available', + 'referral_program' => 'Program referencyjny', + 'referral_code' => 'Referencyjny URL', + 'last_sent_on' => 'Ostatnio wysłany: :date', + 'page_expire' => 'This page will expire soon, :click_here to keep working', + 'upcoming_quotes' => 'Nadchodzące oferty', + 'expired_quotes' => 'Wygaśnięte oferty', + 'sign_up_using' => 'Zarejestruj się przy użyciu', + 'invalid_credentials' => 'These credentials do not match our records', + 'show_all_options' => 'Pokaż wszystkie opcje', + 'user_details' => 'Dane użytkownika', + 'oneclick_login' => 'One-Click Logowanie', + 'disable' => 'Wyłącz', + 'invoice_quote_number' => 'Numery faktur i ofert', + 'invoice_charges' => 'Opłaty faktury', + 'notification_invoice_bounced' => 'We were unable to deliver Invoice :invoice to :contact.', + 'notification_invoice_bounced_subject' => 'Unable to deliver Invoice :invoice', + 'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.', + 'notification_quote_bounced_subject' => 'Unable to deliver Quote :invoice', + 'custom_invoice_link' => 'Custom Invoice Link', + 'total_invoiced' => 'Total Invoiced', + 'open_balance' => 'Open Balance', + 'verify_email' => 'Please visit the link in the account confirmation email to verify your email address.', + 'basic_settings' => 'Ustawienia podstawowe', + 'pro' => 'Pro', + 'gateways' => 'Payment Gateways', + 'next_send_on' => 'Send Next: :date', + 'no_longer_running' => 'This invoice is not scheduled to run', + 'general_settings' => 'General Settings', + 'customize' => 'Customize', + 'oneclick_login_help' => 'Connect an account to login without a password', + 'referral_code_help' => 'Earn money by sharing our app online', + 'enable_with_stripe' => 'Aktywuj | Wymaga Stripe', + 'tax_settings' => 'Tax Settings', + 'create_tax_rate' => 'Dodaj stawkę podatkową', + 'updated_tax_rate' => 'Successfully updated tax rate', + 'created_tax_rate' => 'Successfully created tax rate', + 'edit_tax_rate' => 'Edit tax rate', + 'archive_tax_rate' => 'Archive Tax Rate', + 'archived_tax_rate' => 'Successfully archived the tax rate', + 'default_tax_rate_id' => 'Domyśłna stawka podatkowa', + 'tax_rate' => 'Stawka podatkowa', + 'recurring_hour' => 'Okresowa godzina', + 'pattern' => 'Pattern', + 'pattern_help_title' => 'Pattern Help', + 'pattern_help_1' => 'Create custom invoice and quote numbers by specifying a pattern', + 'pattern_help_2' => 'Available variables:', + 'pattern_help_3' => 'For example, :example would be converted to :value', + 'see_options' => 'See options', + 'invoice_counter' => 'Invoice Counter', + 'quote_counter' => 'Quote Counter', + 'type' => 'Type', + 'activity_1' => ':user created client :client', + 'activity_2' => ':user archived client :client', + 'activity_3' => ':user deleted client :client', + 'activity_4' => ':user created invoice :invoice', + 'activity_5' => ':user updated invoice :invoice', + 'activity_6' => ':user emailed invoice :invoice to :contact', + 'activity_7' => ':contact viewed invoice :invoice', + 'activity_8' => ':user archived invoice :invoice', + 'activity_9' => ':user deleted invoice :invoice', + 'activity_10' => ':contact entered payment :payment for :invoice', + 'activity_11' => ':user updated payment :payment', + 'activity_12' => ':user archived payment :payment', + 'activity_13' => ':user deleted payment :payment', + 'activity_14' => ':user entered :credit credit', + 'activity_15' => ':user updated :credit credit', + 'activity_16' => ':user archived :credit credit', + 'activity_17' => ':user deleted :credit credit', + 'activity_18' => ':user created quote :quote', + 'activity_19' => ':user updated quote :quote', + 'activity_20' => ':user emailed quote :quote to :contact', + 'activity_21' => ':contact viewed quote :quote', + 'activity_22' => ':user archived quote :quote', + 'activity_23' => ':user deleted quote :quote', + 'activity_24' => ':user restored quote :quote', + 'activity_25' => ':user restored invoice :invoice', + 'activity_26' => ':user restored client :client', + 'activity_27' => ':user restored payment :payment', + 'activity_28' => ':user restored :credit credit', + 'activity_29' => ':contact approved quote :quote', + 'activity_30' => ':user created :vendor', + 'activity_31' => ':user created :vendor', + 'activity_32' => ':user created :vendor', + 'activity_33' => ':user created :vendor', + 'activity_34' => ':user created expense :expense', + 'activity_35' => ':user created :vendor', + 'activity_36' => ':user created :vendor', + 'activity_37' => ':user created :vendor', + 'payment' => 'Payment', + 'system' => 'System', + 'signature' => 'Podpis e-mail', + 'default_messages' => 'Default Messages', + 'quote_terms' => 'Warunki oferty', + 'default_quote_terms' => 'Domyślne warunki oferty', + 'default_invoice_terms' => 'Domyślne warunki faktury', + 'default_invoice_footer' => 'Domyślna stopka faktury', + 'quote_footer' => 'Quote Footer', + 'free' => 'Free', + 'quote_is_approved' => 'This quote is approved', + 'apply_credit' => 'Apply Credit', + 'system_settings' => 'System Settings', + 'archive_token' => 'Archive Token', + 'archived_token' => 'Successfully archived token', + 'archive_user' => 'Archive User', + 'archived_user' => 'Successfully archived user', + 'archive_account_gateway' => 'Archive Gateway', + 'archived_account_gateway' => 'Successfully archived gateway', + 'archive_recurring_invoice' => 'Archiwizuj okresową fakturę', + 'archived_recurring_invoice' => 'Okresowa faktura została zarchiwizowana', + 'delete_recurring_invoice' => 'Usuń okresową fakturę', + 'deleted_recurring_invoice' => 'Okresowa faktura została usunięta.', + 'restore_recurring_invoice' => 'Przywróć okresową fakturę', + 'restored_recurring_invoice' => 'Okresowa faktura została przywrócona', + 'archived' => 'Zarchiwizowany', + 'untitled_account' => 'Firma bez nazwy', + 'before' => 'Przed', + 'after' => 'Po', + 'reset_terms_help' => 'Reset to the default account terms', + 'reset_footer_help' => 'Reset to the default account footer', + 'export_data' => 'Exportuj dane', + 'user' => 'Użytkownik', + 'country' => 'Kraj', + 'include' => 'Include', + 'logo_too_large' => 'Your logo is :size, for better PDF performance we suggest uploading an image file less than 200KB', + 'import_freshbooks' => 'Import From FreshBooks', + 'import_data' => 'Import Data', + 'source' => 'Źródło', + 'csv' => 'CSV', + 'client_file' => 'Plik klienta', + 'invoice_file' => 'Plik faktury', + 'task_file' => 'Plik zadania', + 'no_mapper' => 'No valid mapping for file', + 'invalid_csv_header' => 'Invalid CSV Header', + 'client_portal' => 'Portal klienta', + 'admin' => 'Administrator', + 'disabled' => 'Wyłączony', + 'show_archived_users' => 'Pokaż zarchiwizowanych użytkowników', + 'notes' => 'Notatki', + 'invoice_will_create' => 'client will be created', + 'invoices_will_create' => 'invoices will be created', + 'failed_to_import' => 'The following records failed to import, they either already exist or are missing required fields.', + 'publishable_key' => 'Publishable Key', + 'secret_key' => 'Sekretny klucz', + 'missing_publishable_key' => 'Set your Stripe publishable key for an improved checkout process', + 'email_design' => 'Email Design', + 'due_by' => 'Płatny do :date', + 'enable_email_markup' => 'Enable Markup', + 'enable_email_markup_help' => 'Make it easier for your clients to pay you by adding schema.org markup to your emails.', + 'template_help_title' => 'Templates Help', + 'template_help_1' => 'Available variables:', + 'email_design_id' => 'Email Style', + 'email_design_help' => 'Make your emails look more professional with HTML layouts', + 'plain' => 'Plain', + 'light' => 'Light', + 'dark' => 'Dark', + 'industry_help' => 'Used to provide comparisons against the averages of companies of similar size and industry.', + 'subdomain_help' => 'Customize the invoice link subdomain or display the invoice on your own website.', + 'invoice_number_help' => 'Specify a prefix or use a custom pattern to dynamically set the invoice number.', + 'quote_number_help' => 'Specify a prefix or use a custom pattern to dynamically set the quote number.', + 'custom_client_fields_helps' => 'Add a field when creating a client and display the label and value on the PDF.', + 'custom_account_fields_helps' => 'Add a label and value to the company details section of the PDF.', + 'custom_invoice_fields_helps' => 'Add a field when creating an invoice and display the label and value on the PDF.', + 'custom_invoice_charges_helps' => 'Add a field when creating an invoice and include the charge in the invoice subtotals.', + 'token_expired' => 'Validation token was expired. Please try again.', + 'invoice_link' => 'Invoice Link', + 'button_confirmation_message' => 'Click to confirm your email address.', + 'confirm' => 'Confirm', + 'email_preferences' => 'Email Preferences', + 'created_invoices' => 'Successfully created :count invoice(s)', + 'next_invoice_number' => 'The next invoice number is :number.', + 'next_quote_number' => 'The next quote number is :number.', + 'days_before' => 'days before', + 'days_after' => 'days after', + 'field_due_date' => 'termin', + 'field_invoice_date' => 'invoice date', + 'schedule' => 'Schedule', + 'email_designs' => 'Email Designs', + 'assigned_when_sent' => 'Assigned when sent', + 'white_label_purchase_link' => 'Purchase a white label license', + 'expense' => 'Wydatek', + 'expenses' => 'Wydatki', + 'new_expense' => 'Nowy wydatek', + 'enter_expense' => 'Dodaj wydatek', + 'vendors' => 'Vendors', + 'new_vendor' => 'New Vendor', + 'payment_terms_net' => 'Net', + 'vendor' => 'Vendor', + 'edit_vendor' => 'Edit Vendor', + 'archive_vendor' => 'Archive Vendor', + 'delete_vendor' => 'Delete Vendor', + 'view_vendor' => 'View Vendor', + 'deleted_expense' => 'Successfully deleted expense', + 'archived_expense' => 'Successfully archived expense', + 'deleted_expenses' => 'Successfully deleted expenses', + 'archived_expenses' => 'Successfully archived expenses', + 'expense_amount' => 'Expense Amount', + 'expense_balance' => 'Expense Balance', + 'expense_date' => 'Expense Date', + 'expense_should_be_invoiced' => 'Should this expense be invoiced?', + 'public_notes' => 'Public Notes', + 'invoice_amount' => 'Invoice Amount', + 'exchange_rate' => 'Exchange Rate', + 'yes' => 'Yes', + 'no' => 'No', + 'should_be_invoiced' => 'Should be invoiced', + 'view_expense' => 'View expense # :expense', + 'edit_expense' => 'Edit Expense', + 'archive_expense' => 'Archive Expense', + 'delete_expense' => 'Delete Expense', + 'view_expense_num' => 'Expense # :expense', + 'updated_expense' => 'Successfully updated expense', + 'created_expense' => 'Successfully created expense', + 'enter_expense' => 'Dodaj wydatek', + 'view' => 'View', + 'restore_expense' => 'Przywróć wydatek', + 'invoice_expense' => 'Faktura na wydatek', + 'expense_error_multiple_clients' => 'The expenses can\'t belong to different clients', + 'expense_error_invoiced' => 'Expense has already been invoiced', + 'convert_currency' => 'Konwersja waluty', + 'num_days' => 'Liczba dni', + 'create_payment_term' => 'Utwórz warunki płatności', + 'edit_payment_terms' => 'Edytuj warunki płatności', + 'edit_payment_term' => 'Edytuj warunki płatności', + 'archive_payment_term' => 'Zarchiwizuj warunki płatności', + 'recurring_due_dates' => 'Terminy faktur okresowych', + 'recurring_due_date_help' => '

    Automatycznie ustawia termin faktury.

    +

    Invoices on a monthly or yearly cycle set to be due on or before the day they are created will be due the next month. Invoices set to be due on the 29th or 30th in months that don\'t have that day will be due the last day of the month.

    +

    Invoices on a weekly cycle set to be due on the day of the week they are created will be due the next week.

    +

    For example:

    +
      +
    • Today is the 15th, due date is 1st of the month. The due date should likely be the 1st of the next month.
    • +
    • Today is the 15th, due date is the last day of the month. The due date will be the last day of the this month. +
    • +
    • Today is the 15th, due date is the 15th day of the month. The due date will be the 15th day of next month. +
    • +
    • Today is the Friday, due date is the 1st Friday after. The due date will be next Friday, not today. +
    • +
    ', + 'due' => 'Opłata', + 'next_due_on' => 'Następna opłata: :date', + 'use_client_terms' => 'Use client terms', + 'day_of_month' => ':ordinal day of month', + 'last_day_of_month' => 'Last day of month', + 'day_of_week_after' => ':ordinal :day after', + 'sunday' => 'Niedziela', + 'monday' => 'Poniedziałek', + 'tuesday' => 'Wtorek', + 'wednesday' => 'Środa', + 'thursday' => 'Czwartek', + 'friday' => 'Piątek', + 'saturday' => 'Sobota', + 'header_font_id' => 'Czcionka nagłówka', + 'body_font_id' => 'Czcionka', + 'color_font_help' => 'Notatka: Podstawowe kolory i czcionki są wykorzystywane na portalu klienta i w niestandardowych szablonach email-owych.', + 'live_preview' => 'Podgląd', + 'invalid_mail_config' => 'E-mail nie został wysłany. Sprawdź czy ustawienia mailowe są poprawne.', + 'invoice_message_button' => 'Aby wyświetlić fakturę za :amount, kliknij przycisk poniżej.', + 'quote_message_button' => 'Aby wyświetlić swoją ofertę na :amount, kliknij przycisk poniżej.', + 'payment_message_button' => 'Dziekuję za wpłatę :amount.', + 'payment_type_direct_debit' => 'Polecenie zapłaty', + 'bank_accounts' => 'Karty kredytowe i banki', + 'add_bank_account' => 'Dodaj konto bankowe', + 'setup_account' => 'Ustawienia konta', + 'import_expenses' => 'Koszty importu', + 'bank_id' => 'Bank', + 'integration_type' => 'Rodzaj integracji', + 'updated_bank_account' => 'Konto bankowe zostało zaktualizowane', + 'edit_bank_account' => 'Edytuj konto bankowe', + 'archive_bank_account' => 'Archiwizuj konto bankowe', + 'archived_bank_account' => 'Konto bankowe zostało zarchiwizowane.', + 'created_bank_account' => 'Konto bankowe zostało utworzone', + 'validate_bank_account' => 'Zatwierdź konto bankowe', + 'bank_password_help' => 'Notatka: Twoje hasło zostało wysłane bezpiecznie i nie jest przechowywane na naszych serwerach.', + 'bank_password_warning' => 'Ostrzeżenie: Twoje hasło może być wysłane w postaci zwykłego tekstu, rozwaź aktywację protokołu HTTPS.', + 'username' => 'Użytkownik', + 'account_number' => 'Numer konta', + 'account_name' => 'Nazwa konta', + 'bank_account_error' => 'Nie można pobrać danych konta, sprawdź uprawnienia.', + 'status_approved' => 'Zatwierdzono', + 'quote_settings' => 'Ustawienia oferty', + 'auto_convert_quote' => 'Automatycznie konwertuj ofertę', + 'auto_convert_quote_help' => 'Utwórz automatycznie fakturę z oferty zaakceptowanej przez klienta.', + 'validate' => 'Zatwierdź', + 'info' => 'Informacja', + 'imported_expenses' => 'Successfully created :count_vendors vendor(s) and :count_expenses expense(s)', + 'iframe_url_help3' => 'Note: if you plan on accepting credit cards details we strongly recommend enabling HTTPS on your site.', + 'expense_error_multiple_currencies' => 'The expenses can\'t have different currencies.', + 'expense_error_mismatch_currencies' => 'The client\'s currency does not match the expense currency.', + 'trello_roadmap' => 'Trello Roadmap', + 'header_footer' => 'Header/Footer', + 'first_page' => 'Pierwsza strona', + 'all_pages' => 'Wszystkie strony', + 'last_page' => 'Ostatnia strona', + 'all_pages_header' => 'Pokaż nagłówek na', + 'all_pages_footer' => 'Pokaż stopkę na', + 'invoice_currency' => 'Waluta faktury', + 'enable_https' => 'Zalecamy korzystanie z protokołu HTTPS, aby zaakceptować dane karty kredytowej online.', + 'quote_issued_to' => 'Oferta wydana do', + 'show_currency_code' => 'Kod waluty', + 'trial_message' => 'Twoje konto otrzyma bezpłatny dwutygodniowy okres próbny naszego pro planu.', + 'trial_footer' => 'Bezpłatny okres próbny ważny tylko :count dni, aby aktualizować kliknij: :link.', + 'trial_footer_last_day' => 'To ostatni dzień twojego bezpłatnego okresu próbnego, aby zaktualizować kliknij: :link.', + 'trial_call_to_action' => 'Rozpocznij darmowy okres próbny', + 'trial_success' => 'Darmowy okres próbny został włączony', + 'overdue' => 'Zaległy', + + + 'white_label_text' => 'Kup white label licencję na JEDEN ROK za $'.WHITE_LABEL_PRICE.' aby usunąć z portalu klienta logo Invoice Ninja i wesprzeć nasz projekt.', + 'user_email_footer' => 'Aby dostosować ustawienia powiadomień email, zobacz '.SITE_URL.'/settings/notifications', + 'reset_password_footer' => 'If you did not request this password reset please email our support: '.CONTACT_EMAIL, + 'limit_users' => 'Sorry, this will exceed the limit of '.MAX_NUM_USERS.' users', + 'more_designs_self_host_header' => 'Kup 6 szablonów faktur za jedyne $'.INVOICE_DESIGNS_PRICE, + 'old_browser' => 'Proszę użyć nowszej przeglądarki', + 'white_label_custom_css' => ':link for $'.WHITE_LABEL_PRICE.' to enable custom styling and help support our project.', + 'bank_accounts_help' => 'Connect a bank account to automatically import expenses and create vendors. Supports American Express and 400+ US banks.', + 'security' => [ + 'too_many_attempts' => 'Zbyt wiele prób. Spróbuj ponownie za kilka minut.', + 'wrong_credentials' => 'Nieprawidłowy e-mail lub hasło.', + 'confirmation' => 'Twoje konto zostało potwierdzone!', + 'wrong_confirmation' => 'Błędny kod potwierdzający.', + 'password_forgot' => 'Informacje dotyczące resetowania hasła zostały wysłane na Twój adres e-mail.', + 'password_reset' => 'Twoje hasło zostało zmienione.', + 'wrong_password_reset' => 'Nieprawidłowe hasło. Spróbuj ponownie', + ], + 'pro_plan' => [ + 'remove_logo' => ':link to remove the Invoice Ninja logo by joining the Pro Plan', + 'remove_logo_link' => 'Kliknij tutaj', + ], + 'invitation_status' => [ + 'sent' => 'E-mail wysłany', + 'opened' => 'Email otwarty', + 'viewed' => 'Przeglądana faktura', + ], + 'email_errors' => [ + 'inactive_client' => 'E-maile nie mogą być wysyłane do klientów nieaktywnych', + 'inactive_contact' => 'E-mail nie może zostać wysłany do nieaktywnych kontaktów', + 'inactive_invoice' => 'E-mail nie może zostać wysłany do nieaktywnych faktur', + 'user_unregistered' => 'Proszę zarejestrować swoje konto, aby wysyłać e-maile', + 'user_unconfirmed' => 'Proszę potwierdzić swoje konto do wysyłania e-maili', + 'invalid_contact_email' => 'Nieprawidłowy e-mail kontaktowy', + ], + + 'navigation' => 'Nawigacja', + 'list_invoices' => 'Lista faktur', + 'list_clients' => 'Lista klientów', + 'list_quotes' => 'Lista ofert', + 'list_tasks' => 'Lista zadań', + 'list_expenses' => 'Lista wydatków', + 'list_recurring_invoices' => 'Lista faktur okresowych', + 'list_payments' => 'Lista wpłat', + 'list_credits' => 'Lista kredytów', + 'tax_name' => 'Nazwa podatku', + 'report_settings' => 'Ustawienia raportu', + 'search_hotkey' => 'skrót to /', + + 'new_user' => 'Nowy użytkownik', + 'new_product' => 'Nowy produkt', + 'new_tax_rate' => 'Nowa stawka podatkowa', + 'invoiced_amount' => 'Fakturowana kwota', + 'invoice_item_fields' => 'Invoice Item Fields', + 'custom_invoice_item_fields_help' => 'Add a field when creating an invoice item and display the label and value on the PDF.', + 'recurring_invoice_number' => 'Numer faktury okresowej', + 'recurring_invoice_number_prefix_help' => 'Dodaj własny prefix do numeru faktury okresowej. Wartość domyślna to \'R\'.', + + // Client Passwords + 'enable_portal_password'=>'Hasło ochrony faktur', + 'enable_portal_password_help'=>'Allows you to set a password for each contact. If a password is set, the contact will be required to enter a password before viewing invoices.', + 'send_portal_password'=>'Generate password automatically', + 'send_portal_password_help'=>'If no password is set, one will be generated and sent with the first invoice.', + + 'expired' => 'Wygasło', + 'invalid_card_number' => 'Numer karty kredytowej jest nieprawidłowy.', + 'invalid_expiry' => 'Data ważności jest nieprawidłowa.', + 'invalid_cvv' => 'Kod CVV jest nieprawidłowy.', + 'cost' => 'Koszt', + 'create_invoice_for_sample' => 'Notatka: aby zobaczyć podgląd, utwórz fakturę.', + + // User Permissions + 'owner' => 'Właściciel', + 'administrator' => 'Administrator', + 'administrator_help' => 'Allow user to manage users, change settings and modify all records', + 'user_create_all' => 'Create clients, invoices, etc.', + 'user_view_all' => 'View all clients, invoices, etc.', + 'user_edit_all' => 'Edit all clients, invoices, etc.', + 'gateway_help_20' => ':link to sign up for Sage Pay.', + 'gateway_help_21' => ':link to sign up for Sage Pay.', + 'partial_due' => 'Partial Due', + 'restore_vendor' => 'Restore Vendor', + 'restored_vendor' => 'Successfully restored vendor', + 'restored_expense' => 'Successfully restored expense', + 'permissions' => 'Permissions', + 'create_all_help' => 'Allow user to create and modify records', + 'view_all_help' => 'Allow user to view records they didn\'t create', + 'edit_all_help' => 'Allow user to modify records they didn\'t create', + 'view_payment' => 'Zobacz wpłatę', + + 'january' => 'Styczeń', + 'february' => 'Luty', + 'march' => 'Marzec', + 'april' => 'Kwiecień', + 'may' => 'Maj', + 'june' => 'Czerwiec', + 'july' => 'Lipiec', + 'august' => 'Sierpień', + 'september' => 'Wrzesień', + 'october' => 'Październik', + 'november' => 'Listopad', + 'december' => 'Grudzień', + + // Documents + 'documents_header' => 'Dokumenty:', + 'email_documents_header' => 'Dokumenty:', + 'email_documents_example_1' => 'Widgets Receipt.pdf', + 'email_documents_example_2' => 'Final Deliverable.zip', + 'invoice_documents' => 'Dokumenty', + 'expense_documents' => 'Załączone dokumenty', + 'invoice_embed_documents' => 'Embed Documents', + 'invoice_embed_documents_help' => 'Include attached images in the invoice.', + 'document_email_attachment' => 'Załącz dokumenty', + 'download_documents' => 'Ściągnij dokumenty (:size)', + 'documents_from_expenses' => 'From Expenses:', + 'dropzone' => array(// See http://www.dropzonejs.com/#config-dictDefaultMessage + 'DefaultMessage' => 'Upuść pliki lub kliknij, aby przesłać', + 'FallbackMessage' => 'Your browser does not support drag\'n\'drop file uploads.', + 'FallbackText' => 'Please use the fallback form below to upload your files like in the olden days.', + 'FileTooBig' => 'Plik jest zbyt duży ({{filesize}}MiB). Max rozmiar pliku: {{maxFilesize}}MiB.', + 'InvalidFileType' => 'Nie możesz przesłać plików tego typu.', + 'ResponseError' => 'Serwer zwraca {{statusCode}} kod.', + 'CancelUpload' => 'Anuluj przesyłanie', + 'CancelUploadConfirmation' => 'Czy na pewno chcesz anulować przesyłanie pliku?', + 'RemoveFile' => 'Usuń plik', + ), + 'documents' => 'Dokumenty', + 'document_date' => 'Data dokumentu', + 'document_size' => 'Rozmiar', + + 'enable_client_portal' => 'Portal Klienta', + 'enable_client_portal_help' => 'Pokaż/ukryj portal klienta.', + 'enable_client_portal_dashboard' => 'Pulpit', + 'enable_client_portal_dashboard_help' => 'Pokaż/ukryj pulpit w panelu klienta.', + + // Plans + 'account_management' => 'Zarządzanie kontem', + 'plan_status' => 'Plan Status', + + 'plan_upgrade' => 'Aktualizuj', + 'plan_change' => 'Zmień plan', + 'pending_change_to' => 'Zmienia się na', + 'plan_changes_to' => ':plan on :date', + 'plan_term_changes_to' => ':plan (:term) on :date', + 'cancel_plan_change' => 'Anuluj zmianę', + 'plan' => 'Plan', + 'expires' => 'Wygasa', + 'renews' => 'Odnawia', + 'plan_expired' => ':plan Plan Expired', + 'trial_expired' => ':plan Plan Trial Ended', + 'never' => 'Nigdy', + 'plan_free' => 'Darmowy', + 'plan_pro' => 'Pro', + 'plan_enterprise' => 'Enterprise', + 'plan_white_label' => 'Self Hosted (White labeled)', + 'plan_free_self_hosted' => 'Self Hosted (Free)', + 'plan_trial' => 'Trial', + 'plan_term' => 'Term', + 'plan_term_monthly' => 'Miesięcznie', + 'plan_term_yearly' => 'Rocznie', + 'plan_term_month' => 'Miesiąc', + 'plan_term_year' => 'Rok', + 'plan_price_monthly' => '$:price/miesiąc', + 'plan_price_yearly' => '$:price/rok', + 'updated_plan' => 'Ustawienia planu zaktualizowane', + 'plan_paid' => 'Termin rozpoczął', + 'plan_started' => 'Plan rozpoczął', + 'plan_expires' => 'Plan Wygasa', + + 'white_label_button' => 'Biała etykieta', + + 'pro_plan_year_description' => 'One year enrollment in the Invoice Ninja Pro Plan.', + 'pro_plan_month_description' => 'One month enrollment in the Invoice Ninja Pro Plan.', + 'enterprise_plan_product' => 'Plan Enterprise', + 'enterprise_plan_year_description' => 'One year enrollment in the Invoice Ninja Enterprise Plan.', + 'enterprise_plan_month_description' => 'One month enrollment in the Invoice Ninja Enterprise Plan.', + 'plan_credit_product' => 'Kredyt', + 'plan_credit_description' => 'Kredyt za niewykorzystany czas', + 'plan_pending_monthly' => 'Will switch to monthly on :date', + 'plan_refunded' => 'Zwrot został wystawiony.', + + 'live_preview' => 'Podgląd', + 'page_size' => 'Rozmiar strony', + 'live_preview_disabled' => 'Podgląd obrazu na żywo został wyłączony w celu wsparcia wybranej czcionki', + 'invoice_number_padding' => 'Padding', + +); + +return $LANG; + +?>. \ No newline at end of file diff --git a/resources/lang/pl/validation.php b/resources/lang/pl/validation.php new file mode 100644 index 000000000000..d3ffdb6a8155 --- /dev/null +++ b/resources/lang/pl/validation.php @@ -0,0 +1,106 @@ + ":attribute musi być zaakceptowany.", + "active_url" => ":attribute nie jest poprawnym URL-em.", + "after" => ":attribute musi być datą za :date.", + "alpha" => ":attribute może zawierać tylko litery.", + "alpha_dash" => ":attribute może zawierać tylko litery, liczby i myślniki.", + "alpha_num" => ":attribute może zawierać tylko litery i liczby.", + "array" => ":attribute musi być tablicą.", + "before" => ":attribute musi być datą przed :date.", + "between" => array( + "numeric" => ":attribute musi być pomiędzy :min - :max.", + "file" => ":attribute musi mieć rozmiar pomiędzy :min - :max kilobajtów.", + "string" => ":attribute musi mieć pomiędzy :min - :max znaków.", + "array" => ":attribute musi zawierać :min - :max pozycji.", + ), + "confirmed" => ":attribute potwierdzenie nie jest zgodne.", + "date" => ":attribute nie jest prawidłową datą.", + "date_format" => ":attribute nie jest zgodne z formatem :format.", + "different" => ":attribute i :other muszą być różne.", + "digits" => ":attribute musi mieć :digits cyfr.", + "digits_between" => ":attribute musi być w przedziale od :min do :max cyfr.", + "email" => ":attribute format jest nieprawidłowy.", + "exists" => "Zaznaczony :attribute jest niepoprawny.", + "image" => ":attribute musi być zdjęciem.", + "in" => "Zaznaczony :attribute jest niepoprawny.", + "integer" => ":attribute musi być liczbą całkowitą.", + "ip" => ":attribute musi być poprawnym adresem IP.", + "max" => array( + "numeric" => ":attribute nie może być większy niż :max.", + "file" => ":attribute nie może być większy niż :max kilobajtów.", + "string" => ":attribute nie może być dłuższy niż :max znaków.", + "array" => ":attribute nie może zawierać więcej niż :max pozycji.", + ), + "mimes" => ":attribute musi być plikiem o typie: :values.", + "min" => array( + "numeric" => ":attribute musi być przynajmniej :min.", + "file" => ":attribute musi mieć przynajmniej :min kilobajtów.", + "string" => ":attribute musi mieć przynajmniej :min znaków.", + "array" => ":attribute musi zawierać przynajmniej :min pozycji.", + ), + "not_in" => "Zaznaczony :attribute jest niepoprawny.", + "numeric" => ":attribute musi być cyfrą.", + "regex" => ":attribute format jest niepoprawny.", + "required" => ":attribute pole jest wymagane.", + "required_if" => ":attribute pole jest wymagane jeśli :other ma :value.", + "required_with" => ":attribute pole jest wymagane kiedy :values jest obecne.", + "required_without" => ":attribute pole jest wymagane kiedy :values nie występuje.", + "same" => ":attribute i :other muszą być takie same.", + "size" => array( + "numeric" => ":attribute musi mieć :size.", + "file" => ":attribute musi mieć :size kilobajtów.", + "string" => ":attribute musi mieć :size znaków.", + "array" => ":attribute musi zawierać :size pozycji.", + ), + "unique" => ":attribute już istnieje.", + "url" => ":attribute format jest nieprawidłowy.", + + "positive" => ":attribute musi być większe niż zero.", + "has_credit" => "Klient ma niewystarczająco kredytu.", + "notmasked" => "Wartości są maskowane", + "less_than" => ":attribute musi być mniejsze od :value", + "has_counter" => "Wartość musi zawierać {\$counter}", + "valid_contacts" => "Kontakt musi posiadać e-mail lub nazwę", + "valid_invoice_items" => "Faktura przekracza maksymalną kwotę", + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => array(), + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap attribute place-holders + | with something more reader friendly such as E-Mail Address instead + | of "email". This simply helps us make messages a little cleaner. + | + */ + + 'attributes' => array(), + +); \ No newline at end of file From fb473eeb3b35af80d253af98ded4c82250049787 Mon Sep 17 00:00:00 2001 From: emiliolodigiani Date: Tue, 3 May 2016 15:09:13 +0200 Subject: [PATCH 056/120] Update italian translation --- resources/lang/it/texts.php | 140 ++++++++++++++++++------------------ 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/resources/lang/it/texts.php b/resources/lang/it/texts.php index 17d77d5d4881..b4fb1dc2b00d 100644 --- a/resources/lang/it/texts.php +++ b/resources/lang/it/texts.php @@ -382,11 +382,11 @@ return array( 'converted_to_invoice' => 'Il preventivo è stato convertito a fattura con successo', 'quote_subject' => 'Nuovo preventivo da :account', - 'quote_message' => 'Per visualizzare il vostro preventivo per :amount, cliccare il collegamento sotto.', - 'quote_link_message' => 'Per visualizzare il preventivo del vostro cliante cliccate il collegamento sotto:', + 'quote_message' => 'Per visualizzare il vostro preventivo di :amount, cliccate il collegamento sotto.', + 'quote_link_message' => 'Per visualizzare il preventivo del vostro cliente cliccate il collegamento sotto:', 'notification_quote_sent_subject' => 'Il preventivo :invoice è stato inviato a :client', 'notification_quote_viewed_subject' => 'Il preventivo :invoice è stato visualizzato da :client', - 'notification_quote_sent' => 'Al seguente cliente :client è stata inviata la fattura :invoice per :amount.', + 'notification_quote_sent' => 'Al seguente cliente :client è stata inviato il preventivo :invoice per un importo di :amount.', 'notification_quote_viewed' => 'Il seguente cliente :client ha visualizzato il preventivo :invoice di :amount.', 'session_expired' => 'La vostra sessione è scaduta.', @@ -489,7 +489,7 @@ return array( 'invoice_history' => 'Invoice History', 'quote_history' => 'Quote History', 'current_version' => 'Current version', - 'select_version' => 'Select version', + 'select_versiony' => 'Select version', 'view_history' => 'View History', 'edit_payment' => 'Edit Payment', @@ -728,12 +728,12 @@ return array( 'header' => 'Header', 'footer' => 'Footer', 'custom' => 'Custom', - 'invoice_to' => 'Invoice to', - 'invoice_no' => 'Invoice No.', - 'recent_payments' => 'Recent Payments', - 'outstanding' => 'Outstanding', - 'manage_companies' => 'Manage Companies', - 'total_revenue' => 'Total Revenue', + 'invoice_to' => 'Fattura a', + 'invoice_no' => 'Fattura N.', + 'recent_payments' => 'Pagamenti recenti', + 'outstanding' => 'Inevaso', + 'manage_companies' => 'Gestisci aziende', + 'total_revenue' => 'Ricavo totale', 'current_user' => 'Current User', 'new_recurring_invoice' => 'Nuova Fattura Ricorrente', @@ -784,7 +784,7 @@ return array( 'last_sent_on' => 'Last sent on :date', 'page_expire' => 'This page will expire soon, :click_here to keep working', - 'upcoming_quotes' => 'Upcoming Quotes', + 'upcoming_quotes' => 'Preventivi in scadenza', 'expired_quotes' => 'Preventivi Scaduti', 'sign_up_using' => 'Sign up using', @@ -842,19 +842,19 @@ return array( 'quote_counter' => 'Quote Counter', 'type' => 'Type', - 'activity_1' => ':user created client :client', - 'activity_2' => ':user archived client :client', + 'activity_1' => ':user ha creato il cliente :client', + 'activity_2' => ':user ha archiviato il cliente :client', 'activity_3' => ':user deleted client :client', 'activity_4' => ':user ha creato la fattura :invoice', 'activity_5' => ':user ha aggiornato la fattura :invoice', - 'activity_6' => ':user emailed invoice :invoice to :contact', - 'activity_7' => ':contact viewed invoice :invoice', - 'activity_8' => ':user archived invoice :invoice', - 'activity_9' => ':user deleted invoice :invoice', - 'activity_10' => ':contact entered payment :payment for :invoice', - 'activity_11' => ':user updated payment :payment', - 'activity_12' => ':user archived payment :payment', - 'activity_13' => ':user deleted payment :payment', + 'activity_6' => ':user ha inviato per email la fattura :invoice a :contact', + 'activity_7' => ':contact ha visto la fattura :invoice', + 'activity_8' => ':user ha archiviato la fattura :invoice', + 'activity_9' => ':user ha cancellato la fattura :invoice', + 'activity_10' => ':contact ha inserito il pagamento :payment per :invoice', + 'activity_11' => ':user ha aggiornato il pagamento :payment', + 'activity_12' => ':user ha archiviato il pagamento :payment', + 'activity_13' => ':user ha cancellato il pagamento :payment', 'activity_14' => ':user entered :credit credit', 'activity_15' => ':user updated :credit credit', 'activity_16' => ':user archived :credit credit', @@ -862,7 +862,7 @@ return array( 'activity_18' => ':user created quote :quote', 'activity_19' => ':user updated quote :quote', 'activity_20' => ':user emailed quote :quote to :contact', - 'activity_21' => ':contact viewed quote :quote', + 'activity_21' => ':contact ha visto il preventivo :quote', 'activity_22' => ':user archived quote :quote', 'activity_23' => ':user deleted quote :quote', 'activity_24' => ':user restored quote :quote', @@ -870,7 +870,7 @@ return array( 'activity_26' => ':user restored client :client', 'activity_27' => ':user restored payment :payment', 'activity_28' => ':user restored :credit credit', - 'activity_29' => ':contact approved quote :quote', + 'activity_29' => ':contact ha approvato la fattura :quote', 'payment' => 'Payment', 'system' => 'System', @@ -1004,33 +1004,33 @@ return array( 'archived_expenses' => 'Successfully archived expenses', // Expenses - 'expense_amount' => 'Expense Amount', - 'expense_balance' => 'Expense Balance', - 'expense_date' => 'Expense Date', - 'expense_should_be_invoiced' => 'Should this expense be invoiced?', - 'public_notes' => 'Public Notes', - 'invoice_amount' => 'Invoice Amount', - 'exchange_rate' => 'Exchange Rate', - 'yes' => 'Yes', + 'expense_amount' => 'Importo Spesa', + 'expense_balance' => 'Bilancio Spesa', + 'expense_date' => 'Data Spesa', + 'expense_should_be_invoiced' => 'Questa spesa deve essere fatturata?', + 'public_notes' => 'Note Pubbliche (Descrizione in fattura)', + 'invoice_amount' => 'Importo Fattura', + 'exchange_rate' => 'Tasso di Cambio', + 'yes' => 'Si', 'no' => 'No', - 'should_be_invoiced' => 'Should be invoiced', - 'view_expense' => 'View expense # :expense', - 'edit_expense' => 'Edit Expense', - 'archive_expense' => 'Archive Expense', - 'delete_expense' => 'Delete Expense', - 'view_expense_num' => 'Expense # :expense', - 'updated_expense' => 'Successfully updated expense', - 'created_expense' => 'Successfully created expense', - 'enter_expense' => 'Enter Expense', - 'view' => 'View', - 'restore_expense' => 'Restore Expense', - 'invoice_expense' => 'Invoice Expense', - 'expense_error_multiple_clients' =>'The expenses can\'t belong to different clients', - 'expense_error_invoiced' => 'Expense has already been invoiced', - 'convert_currency' => 'Convert currency', + 'should_be_invoiced' => 'Deve essere fatturata', + 'view_expense' => 'Vedi spesa # :expense', + 'edit_expense' => 'Modifica Spesa', + 'archive_expense' => 'Archivia Spesa', + 'delete_expense' => 'Cancella Spesa', + 'view_expense_num' => 'Spesa # :expense', + 'updated_expense' => 'Spesa aggiornata con successo', + 'created_expense' => 'Spesa creata con successo', + 'enter_expense' => 'Inserisci Spesa', + 'view' => 'Vedi', + 'restore_expense' => 'Ripristina Spesa', + 'invoice_expense' => 'Fattura Spesa', + 'expense_error_multiple_clients' =>'Le spese non possono appartenere a clienti differenti', + 'expense_error_invoiced' => 'La spesa è stata già fatturata', + 'convert_currency' => 'Converti valuta', // Payment terms - 'num_days' => 'Number of days', + 'num_days' => 'Numero di giorni', 'create_payment_term' => 'Create Payment Term', 'edit_payment_terms' => 'Edit Payment Term', 'edit_payment_term' => 'Edit Payment Term', @@ -1053,17 +1053,17 @@ return array( ', 'due' => 'Due', 'next_due_on' => 'Due Next: :date', - 'use_client_terms' => 'Use client terms', - 'day_of_month' => ':ordinal day of month', - 'last_day_of_month' => 'Last day of month', - 'day_of_week_after' => ':ordinal :day after', - 'sunday' => 'Sunday', - 'monday' => 'Monday', - 'tuesday' => 'Tuesday', - 'wednesday' => 'Wednesday', - 'thursday' => 'Thursday', - 'friday' => 'Friday', - 'saturday' => 'Saturday', + 'use_client_terms' => 'Usa i termini del cliente', + 'day_of_month' => ':ordinal giorno del mese', + 'last_day_of_month' => 'L\'ultimo giorno del mese', + 'day_of_week_after' => ':ordinal :day dopo', + 'sunday' => 'Domenica', + 'monday' => 'Lunedì', + 'tuesday' => 'Martedì', + 'wednesday' => 'Mercoledì', + 'thursday' => 'Giovedì', + 'friday' => 'Venerdì', + 'saturday' => 'Sabato', // Fonts 'header_font_id' => 'Header Font', @@ -1182,17 +1182,17 @@ return array( 'edit_all_help' => 'Allow user to modify records they didn\'t create', 'view_payment' => 'View Payment', - 'january' => 'January', - 'february' => 'February', - 'march' => 'March', - 'april' => 'April', - 'may' => 'May', - 'june' => 'June', - 'july' => 'July', - 'august' => 'August', - 'september' => 'September', - 'october' => 'October', - 'november' => 'November', - 'december' => 'December', + 'january' => 'Gennaio', + 'february' => 'Febbraio', + 'march' => 'Marzo', + 'april' => 'Aprile', + 'may' => 'Maggio', + 'june' => 'Giugno', + 'july' => 'Luglio', + 'august' => 'Agosto', + 'september' => 'Settembre', + 'october' => 'Ottobre', + 'november' => 'Novembre', + 'december' => 'Dicembre', ); From 5295bf30fc56d3f31149bc3b40b0098b4cfbed98 Mon Sep 17 00:00:00 2001 From: emiliolodigiani Date: Tue, 3 May 2016 15:11:34 +0200 Subject: [PATCH 057/120] Revert typo fix --- resources/lang/it/texts.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lang/it/texts.php b/resources/lang/it/texts.php index b4fb1dc2b00d..526ce14013ba 100644 --- a/resources/lang/it/texts.php +++ b/resources/lang/it/texts.php @@ -489,7 +489,7 @@ return array( 'invoice_history' => 'Invoice History', 'quote_history' => 'Quote History', 'current_version' => 'Current version', - 'select_versiony' => 'Select version', + 'select_version' => 'Select version', 'view_history' => 'View History', 'edit_payment' => 'Edit Payment', From c0a5084282e039f7375d27bb5464b9734c1cb34d Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 3 May 2016 19:06:38 +0300 Subject: [PATCH 058/120] Added details to cybersource error --- app/Http/Controllers/PaymentController.php | 3 ++- app/Ninja/Transformers/AccountTransformer.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 12537dbebaa1..165f54ef9ed0 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -551,7 +551,8 @@ class PaymentController extends BaseController $payment = $this->paymentService->createPayment($invitation, $accountGateway, $token, $payerId); Session::flash('message', trans('texts.applied_payment')); } else { - Session::flash('error', Input::get('message')); + $message = Input::get('message') . ': ' . Input::get('invalid_fields'); + Session::flash('error', $message); } return Redirect::to($invitation->getLink()); } elseif (method_exists($gateway, 'completePurchase') diff --git a/app/Ninja/Transformers/AccountTransformer.php b/app/Ninja/Transformers/AccountTransformer.php index 6a1c32f30e09..f56635bad2b1 100644 --- a/app/Ninja/Transformers/AccountTransformer.php +++ b/app/Ninja/Transformers/AccountTransformer.php @@ -14,12 +14,12 @@ class AccountTransformer extends EntityTransformer 'users', 'products', 'taxRates', - 'payments' ]; protected $availableIncludes = [ 'clients', 'invoices', + 'payments', ]; public function includeUsers(Account $account) From 5218568c918900bc3010745a69c4af47df9824a5 Mon Sep 17 00:00:00 2001 From: Bartlomiej Szala Date: Tue, 3 May 2016 18:40:21 +0200 Subject: [PATCH 059/120] Remove transaction migration --- ...16_05_02_071859_add_polish_translation.php | 29 ------------------- 1 file changed, 29 deletions(-) delete mode 100644 database/migrations/2016_05_02_071859_add_polish_translation.php diff --git a/database/migrations/2016_05_02_071859_add_polish_translation.php b/database/migrations/2016_05_02_071859_add_polish_translation.php deleted file mode 100644 index b78969cfbbde..000000000000 --- a/database/migrations/2016_05_02_071859_add_polish_translation.php +++ /dev/null @@ -1,29 +0,0 @@ -insert(['name' => 'Polish', 'locale' => self::LANGUAGE_LOCALE]); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - $language = \App\Models\Language::whereLocale(self::LANGUAGE_LOCALE)->first(); - $language->delete(); - } -} From 2d878b6480f674bc99f5d80bb91e9d79ff796347 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 3 May 2016 23:02:29 +0300 Subject: [PATCH 060/120] API optimizations --- app/Http/Controllers/BaseAPIController.php | 9 +-------- app/Http/Controllers/PaymentApiController.php | 2 +- app/Models/InvoiceItem.php | 5 +++++ app/Models/Product.php | 5 +++++ app/Models/TaxRate.php | 5 +++++ app/Ninja/Transformers/ClientTransformer.php | 6 ++---- app/Ninja/Transformers/ContactTransformer.php | 5 ++--- app/Ninja/Transformers/CreditTransformer.php | 5 ++--- app/Ninja/Transformers/DocumentTransformer.php | 6 ++---- app/Ninja/Transformers/EntityTransformer.php | 15 +++++++++++++++ app/Ninja/Transformers/ExpenseTransformer.php | 5 ++--- app/Ninja/Transformers/InvoiceItemTransformer.php | 6 ++---- app/Ninja/Transformers/InvoiceTransformer.php | 6 ++---- app/Ninja/Transformers/PaymentTransformer.php | 6 ++---- app/Ninja/Transformers/ProductTransformer.php | 5 ++--- app/Ninja/Transformers/TaskTransformer.php | 6 ++---- app/Ninja/Transformers/TaxRateTransformer.php | 5 ++--- .../Transformers/VendorContactTransformer.php | 5 ++--- app/Ninja/Transformers/VendorTransformer.php | 6 ++---- 19 files changed, 58 insertions(+), 55 deletions(-) diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index db99a3beaf1d..8e48f3bff878 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -193,26 +193,19 @@ class BaseAPIController extends Controller protected function getRequestIncludes($data) { - $data[] = 'user'; - $included = Request::get('include'); $included = explode(',', $included); foreach ($included as $include) { if ($include == 'invoices') { $data[] = 'invoices.invoice_items'; - $data[] = 'invoices.user'; } elseif ($include == 'client') { $data[] = 'client.contacts'; - $data[] = 'client.user'; } elseif ($include == 'clients') { $data[] = 'clients.contacts'; - $data[] = 'clients.user'; } elseif ($include == 'vendors') { $data[] = 'vendors.vendorcontacts'; - $data[] = 'vendors.user'; - } - elseif ($include) { + } elseif ($include) { $data[] = $include; } } diff --git a/app/Http/Controllers/PaymentApiController.php b/app/Http/Controllers/PaymentApiController.php index 0dd8de50803b..3355b6bff8f4 100644 --- a/app/Http/Controllers/PaymentApiController.php +++ b/app/Http/Controllers/PaymentApiController.php @@ -49,7 +49,7 @@ class PaymentApiController extends BaseAPIController { $payments = Payment::scope() ->withTrashed() - ->with(['invoice']) + ->with(['invoice']) ->orderBy('created_at', 'desc'); return $this->listResponse($payments); diff --git a/app/Models/InvoiceItem.php b/app/Models/InvoiceItem.php index d3981c931c62..e80482bafdb8 100644 --- a/app/Models/InvoiceItem.php +++ b/app/Models/InvoiceItem.php @@ -19,6 +19,11 @@ class InvoiceItem extends EntityModel return $this->belongsTo('App\Models\Invoice'); } + public function user() + { + return $this->belongsTo('App\Models\User')->withTrashed(); + } + public function product() { return $this->belongsTo('App\Models\Product'); diff --git a/app/Models/Product.php b/app/Models/Product.php index 0d3221f2a1ea..05944c9fff94 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -26,6 +26,11 @@ class Product extends EntityModel return Product::scope()->where('product_key', '=', $key)->first(); } + public function user() + { + return $this->belongsTo('App\Models\User')->withTrashed(); + } + public function default_tax_rate() { return $this->belongsTo('App\Models\TaxRate'); diff --git a/app/Models/TaxRate.php b/app/Models/TaxRate.php index 72ad266b07d8..384ccf933b36 100644 --- a/app/Models/TaxRate.php +++ b/app/Models/TaxRate.php @@ -17,4 +17,9 @@ class TaxRate extends EntityModel { return ENTITY_TAX_RATE; } + + public function user() + { + return $this->belongsTo('App\Models\User')->withTrashed(); + } } diff --git a/app/Ninja/Transformers/ClientTransformer.php b/app/Ninja/Transformers/ClientTransformer.php index e9a63e07d693..63f0baa0c871 100644 --- a/app/Ninja/Transformers/ClientTransformer.php +++ b/app/Ninja/Transformers/ClientTransformer.php @@ -77,13 +77,11 @@ class ClientTransformer extends EntityTransformer public function transform(Client $client) { - return [ + return array_merge($this->getDefaults($client), [ 'id' => (int) $client->public_id, 'name' => $client->name, 'balance' => (float) $client->balance, 'paid_to_date' => (float) $client->paid_to_date, - 'user_id' => (int) $client->user->public_id + 1, - 'account_key' => $this->account->account_key, 'updated_at' => $this->getTimestamp($client->updated_at), 'archived_at' => $this->getTimestamp($client->deleted_at), 'address1' => $client->address1, @@ -106,6 +104,6 @@ class ClientTransformer extends EntityTransformer 'currency_id' => (int) $client->currency_id, 'custom_value1' => $client->custom_value1, 'custom_value2' => $client->custom_value2, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/ContactTransformer.php b/app/Ninja/Transformers/ContactTransformer.php index e404b73660d4..68172e156c60 100644 --- a/app/Ninja/Transformers/ContactTransformer.php +++ b/app/Ninja/Transformers/ContactTransformer.php @@ -8,7 +8,7 @@ class ContactTransformer extends EntityTransformer { public function transform(Contact $contact) { - return [ + return array_merge($this->getDefaults($contact), [ 'id' => (int) $contact->public_id, 'first_name' => $contact->first_name, 'last_name' => $contact->last_name, @@ -18,8 +18,7 @@ class ContactTransformer extends EntityTransformer 'is_primary' => (bool) $contact->is_primary, 'phone' => $contact->phone, 'last_login' => $contact->last_login, - 'account_key' => $this->account->account_key, 'send_invoice' => (bool) $contact->send_invoice, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/CreditTransformer.php b/app/Ninja/Transformers/CreditTransformer.php index a33185d2ff39..39ce5ff1d8f3 100644 --- a/app/Ninja/Transformers/CreditTransformer.php +++ b/app/Ninja/Transformers/CreditTransformer.php @@ -8,17 +8,16 @@ class CreditTransformer extends EntityTransformer { public function transform(Credit $credit) { - return [ + return array_merge($this->getDefaults($credit), [ 'id' => (int) $credit->public_id, 'amount' => (float) $credit->amount, 'balance' => (float) $credit->balance, 'updated_at' => $this->getTimestamp($credit->updated_at), 'archived_at' => $this->getTimestamp($credit->deleted_at), 'is_deleted' => (bool) $credit->is_deleted, - 'account_key' => $this->account->account_key, 'credit_date' => $credit->credit_date, 'credit_number' => $credit->credit_number, 'private_notes' => $credit->private_notes, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/DocumentTransformer.php b/app/Ninja/Transformers/DocumentTransformer.php index 61d18506eba1..4cbfa0619193 100644 --- a/app/Ninja/Transformers/DocumentTransformer.php +++ b/app/Ninja/Transformers/DocumentTransformer.php @@ -8,14 +8,12 @@ class DocumentTransformer extends EntityTransformer { public function transform(Document $document) { - - return [ + return array_merge($this->getDefaults($document), [ 'id' => (int) $document->public_id, 'name' => $document->name, - 'account_key' => $this->account->account_key, 'type' => $document->type, 'invoice_id' => isset($document->invoice->public_id) ? (int) $document->invoice->public_id : null, 'expense_id' => isset($document->expense->public_id) ? (int) $document->expense->public_id : null, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/EntityTransformer.php b/app/Ninja/Transformers/EntityTransformer.php index af1304acd67f..d2d02bd0b6ee 100644 --- a/app/Ninja/Transformers/EntityTransformer.php +++ b/app/Ninja/Transformers/EntityTransformer.php @@ -1,5 +1,6 @@ defaultIncludes; } + + protected function getDefaults($entity) + { + $data = [ + 'account_key' => $this->account->account_key, + 'is_owner' => (bool) Auth::user()->owns($entity), + ]; + + if ($entity->relationLoaded('user')) { + $data['user_id'] = (int) $entity->user->public_id + 1; + } + + return $data; + } } diff --git a/app/Ninja/Transformers/ExpenseTransformer.php b/app/Ninja/Transformers/ExpenseTransformer.php index 8f2e02c0a88f..46c334cb3e55 100644 --- a/app/Ninja/Transformers/ExpenseTransformer.php +++ b/app/Ninja/Transformers/ExpenseTransformer.php @@ -15,7 +15,7 @@ class ExpenseTransformer extends EntityTransformer public function transform(Expense $expense) { - return [ + return array_merge($this->getDefaults($expense), [ 'id' => (int) $expense->public_id, 'private_notes' => $expense->private_notes, 'public_notes' => $expense->public_notes, @@ -25,7 +25,6 @@ class ExpenseTransformer extends EntityTransformer 'transaction_id' => $expense->transaction_id, 'bank_id' => $expense->bank_id, 'expense_currency_id' => (int) $expense->expense_currency_id, - 'account_key' => $this->account->account_key, 'amount' => (float) $expense->amount, 'expense_date' => $expense->expense_date, 'exchange_rate' => (float) $expense->exchange_rate, @@ -34,6 +33,6 @@ class ExpenseTransformer extends EntityTransformer 'client_id' => $this->client ? $this->client->public_id : (isset($expense->client->public_id) ? (int) $expense->client->public_id : null), 'invoice_id' => isset($expense->invoice->public_id) ? (int) $expense->invoice->public_id : null, 'vendor_id' => isset($expense->vendor->public_id) ? (int) $expense->vendor->public_id : null, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/InvoiceItemTransformer.php b/app/Ninja/Transformers/InvoiceItemTransformer.php index 895d8e8d2f4a..080234f35e1b 100644 --- a/app/Ninja/Transformers/InvoiceItemTransformer.php +++ b/app/Ninja/Transformers/InvoiceItemTransformer.php @@ -8,11 +8,9 @@ class InvoiceItemTransformer extends EntityTransformer { public function transform(InvoiceItem $item) { - return [ + return array_merge($this->getDefaults($item), [ 'id' => (int) $item->public_id, 'product_key' => $item->product_key, - 'account_key' => $this->account->account_key, - 'user_id' => (int) $item->user_id, 'updated_at' => $this->getTimestamp($item->updated_at), 'archived_at' => $this->getTimestamp($item->deleted_at), 'product_key' => $item->product_key, @@ -23,6 +21,6 @@ class InvoiceItemTransformer extends EntityTransformer 'tax_rate1' => (float) $item->tax_rate1, 'tax_name2' => $item->tax_name2 ? $item->tax_name1 : '', 'tax_rate2' => (float) $item->tax_rate2, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/InvoiceTransformer.php b/app/Ninja/Transformers/InvoiceTransformer.php index 51af3d029936..0933422875c2 100644 --- a/app/Ninja/Transformers/InvoiceTransformer.php +++ b/app/Ninja/Transformers/InvoiceTransformer.php @@ -71,7 +71,7 @@ class InvoiceTransformer extends EntityTransformer public function transform(Invoice $invoice) { - return [ + return array_merge($this->getDefaults($invoice), [ 'id' => (int) $invoice->public_id, 'amount' => (float) $invoice->amount, 'balance' => (float) $invoice->balance, @@ -105,8 +105,6 @@ class InvoiceTransformer extends EntityTransformer 'partial' => (float) $invoice->partial, 'has_tasks' => (bool) $invoice->has_tasks, 'auto_bill' => (bool) $invoice->auto_bill, - 'account_key' => $this->account->account_key, - 'user_id' => (int) $invoice->user->public_id + 1, 'custom_value1' => (float) $invoice->custom_value1, 'custom_value2' => (float) $invoice->custom_value2, 'custom_taxes1' => (bool) $invoice->custom_taxes1, @@ -115,6 +113,6 @@ class InvoiceTransformer extends EntityTransformer 'quote_invoice_id' => (int) $invoice->quote_invoice_id, 'custom_text_value1' => $invoice->custom_text_value1, 'custom_text_value2' => $invoice->custom_text_value2, - ]; + ]); } } diff --git a/app/Ninja/Transformers/PaymentTransformer.php b/app/Ninja/Transformers/PaymentTransformer.php index 8ea2dcbae051..c4e4328cb845 100644 --- a/app/Ninja/Transformers/PaymentTransformer.php +++ b/app/Ninja/Transformers/PaymentTransformer.php @@ -47,11 +47,9 @@ class PaymentTransformer extends EntityTransformer public function transform(Payment $payment) { - return [ + return array_merge($this->getDefaults($payment), [ 'id' => (int) $payment->public_id, 'amount' => (float) $payment->amount, - 'account_key' => $this->account->account_key, - 'user_id' => (int) $payment->user->public_id + 1, 'transaction_reference' => $payment->transaction_reference, 'payment_date' => $payment->payment_date, 'updated_at' => $this->getTimestamp($payment->updated_at), @@ -59,6 +57,6 @@ class PaymentTransformer extends EntityTransformer 'is_deleted' => (bool) $payment->is_deleted, 'payment_type_id' => (int) $payment->payment_type_id, 'invoice_id' => (int) ($this->invoice ? $this->invoice->public_id : $payment->invoice->public_id), - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/ProductTransformer.php b/app/Ninja/Transformers/ProductTransformer.php index 34fbcf7f18ff..8331d7666154 100644 --- a/app/Ninja/Transformers/ProductTransformer.php +++ b/app/Ninja/Transformers/ProductTransformer.php @@ -7,16 +7,15 @@ class ProductTransformer extends EntityTransformer { public function transform(Product $product) { - return [ + return array_merge($this->getDefaults(), [ 'id' => (int) $product->public_id, 'product_key' => $product->product_key, 'notes' => $product->notes, 'cost' => $product->cost, 'qty' => $product->qty, - 'account_key' =>$this->account->account_key, 'default_tax_rate_id' =>$product->default_tax_rate_id, 'updated_at' =>$this->getTimestamp($product->updated_at), 'archived_at' => $this->getTimestamp($product->deleted_at), - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/TaskTransformer.php b/app/Ninja/Transformers/TaskTransformer.php index 908a8118aaea..7bfc474a72f2 100644 --- a/app/Ninja/Transformers/TaskTransformer.php +++ b/app/Ninja/Transformers/TaskTransformer.php @@ -39,12 +39,10 @@ class TaskTransformer extends EntityTransformer public function transform(Task $task) { - return [ + return array_merge($this->getDefaults($task), [ 'id' => (int) $task->public_id, - 'account_key' => $this->account->account_key, - 'user_id' => (int) $task->user->public_id + 1, 'description' => $task->description, 'duration' => $task->getDuration() - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/TaxRateTransformer.php b/app/Ninja/Transformers/TaxRateTransformer.php index f7d307bf7bb4..a0c5aab539a0 100644 --- a/app/Ninja/Transformers/TaxRateTransformer.php +++ b/app/Ninja/Transformers/TaxRateTransformer.php @@ -21,13 +21,12 @@ class TaxRateTransformer extends EntityTransformer public function transform(TaxRate $taxRate) { - return [ + return array_merge($this->getDefaults($taxRate), [ 'id' => (int) $taxRate->public_id, 'name' => $taxRate->name, 'rate' => (float) $taxRate->rate, 'updated_at' => $this->getTimestamp($taxRate->updated_at), 'archived_at' => $this->getTimestamp($taxRate->deleted_at), - 'account_key' => $this->account->account_key, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/VendorContactTransformer.php b/app/Ninja/Transformers/VendorContactTransformer.php index 3b75aee53a28..f277964cec28 100644 --- a/app/Ninja/Transformers/VendorContactTransformer.php +++ b/app/Ninja/Transformers/VendorContactTransformer.php @@ -8,7 +8,7 @@ class VendorContactTransformer extends EntityTransformer { public function transform(VendorContact $contact) { - return [ + return array_merge($this->getDefaults($contact), [ 'id' => (int) $contact->public_id, 'first_name' => $contact->first_name, 'last_name' => $contact->last_name, @@ -17,7 +17,6 @@ class VendorContactTransformer extends EntityTransformer 'archived_at' => $this->getTimestamp($contact->deleted_at), 'is_primary' => (bool) $contact->is_primary, 'phone' => $contact->phone, - 'account_key' => $this->account->account_key, - ]; + ]); } } \ No newline at end of file diff --git a/app/Ninja/Transformers/VendorTransformer.php b/app/Ninja/Transformers/VendorTransformer.php index f0b8fd0415f0..f9c37ac44200 100644 --- a/app/Ninja/Transformers/VendorTransformer.php +++ b/app/Ninja/Transformers/VendorTransformer.php @@ -61,13 +61,11 @@ class VendorTransformer extends EntityTransformer public function transform(Vendor $vendor) { - return [ + return array_merge($this->getDefaults($vendor), [ 'id' => (int) $vendor->public_id, 'name' => $vendor->name, 'balance' => (float) $vendor->balance, 'paid_to_date' => (float) $vendor->paid_to_date, - 'user_id' => (int) $vendor->user->public_id + 1, - 'account_key' => $this->account->account_key, 'updated_at' => $this->getTimestamp($vendor->updated_at), 'archived_at' => $this->getTimestamp($vendor->deleted_at), 'address1' => $vendor->address1, @@ -84,6 +82,6 @@ class VendorTransformer extends EntityTransformer 'vat_number' => $vendor->vat_number, 'id_number' => $vendor->id_number, 'currency_id' => (int) $vendor->currency_id - ]; + ]); } } \ No newline at end of file From dfa6c518abb8529e8f55b38e5ef0f42e80b4a4a9 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 3 May 2016 23:15:41 +0300 Subject: [PATCH 061/120] Fix for travis test --- tests/acceptance/ExpenseCest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/acceptance/ExpenseCest.php b/tests/acceptance/ExpenseCest.php index e22bebac3b7f..770125574d9e 100644 --- a/tests/acceptance/ExpenseCest.php +++ b/tests/acceptance/ExpenseCest.php @@ -44,13 +44,14 @@ class ExpenseCest $I->selectDropdown($I, $vendorName, '.vendor-select .dropdown-toggle'); $I->selectDropdown($I, $clientEmail, '.client-select .dropdown-toggle'); $I->click('Save'); + $I->wait(2); $I->seeInDatabase('expenses', ['vendor_id' => $vendorId]); // invoice expense $I->executeJS('submitAction(\'invoice\')'); - $I->wait(3); + $I->wait(2); $I->click('Save'); - $I->wait(3); + $I->wait(2); $I->see($clientEmail); $I->see($amount); } From 2fcb1d66a5ec9230e197abde576517441a9deeca Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 4 May 2016 09:53:43 +0300 Subject: [PATCH 062/120] Fix for travis tests --- .travis.yml | 3 ++- app/Exceptions/Handler.php | 2 +- app/Libraries/Utils.php | 5 +++++ app/Ninja/Transformers/ProductTransformer.php | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 083bbe4f2583..1f66f7aa29f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,7 +45,8 @@ before_script: - php artisan key:generate --no-interaction - sed -i 's/APP_ENV=production/APP_ENV=development/g' .env - sed -i 's/APP_DEBUG=false/APP_DEBUG=true/g' .env - - sed -i 's/REQUIRE_HTTPS=false/NINJA_DEV=true/g' .env + - sed -i '$a NINJA_DEV=true' .env + - sed -i '$a TRAVIS=true' .env # create the database and user - mysql -u root -e "create database IF NOT EXISTS ninja;" - mysql -u root -e "GRANT ALL PRIVILEGES ON ninja.* To 'ninja'@'localhost' IDENTIFIED BY 'ninja'; FLUSH PRIVILEGES;" diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 5333d90f9e3b..2a8b0d305277 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -39,7 +39,7 @@ class Handler extends ExceptionHandler { return false; } - if (Utils::isNinja()) { + if (Utils::isNinja() && ! Utils::isTravis()) { Utils::logError(Utils::getErrorString($e)); return false; } else { diff --git a/app/Libraries/Utils.php b/app/Libraries/Utils.php index 141eacfc3058..7c1b11dfd84b 100644 --- a/app/Libraries/Utils.php +++ b/app/Libraries/Utils.php @@ -51,6 +51,11 @@ class Utils return php_sapi_name() == 'cli'; } + public static function isTravis() + { + return env('TRAVIS') == 'true'; + } + public static function isNinja() { return self::isNinjaProd() || self::isNinjaDev(); diff --git a/app/Ninja/Transformers/ProductTransformer.php b/app/Ninja/Transformers/ProductTransformer.php index 8331d7666154..309305815fb0 100644 --- a/app/Ninja/Transformers/ProductTransformer.php +++ b/app/Ninja/Transformers/ProductTransformer.php @@ -7,7 +7,7 @@ class ProductTransformer extends EntityTransformer { public function transform(Product $product) { - return array_merge($this->getDefaults(), [ + return array_merge($this->getDefaults($product), [ 'id' => (int) $product->public_id, 'product_key' => $product->product_key, 'notes' => $product->notes, From 2a6876a902fceeab4a7e13bb41d771bd1ecd9258 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 4 May 2016 11:14:20 +0300 Subject: [PATCH 063/120] Fix for travis tests --- .../Requests/CreatePaymentTermRequest.php | 30 ------------------- .../Requests/UpdatePaymentTermRequest.php | 30 ------------------- 2 files changed, 60 deletions(-) delete mode 100644 app/Http/Requests/CreatePaymentTermRequest.php delete mode 100644 app/Http/Requests/UpdatePaymentTermRequest.php diff --git a/app/Http/Requests/CreatePaymentTermRequest.php b/app/Http/Requests/CreatePaymentTermRequest.php deleted file mode 100644 index 23bf3151d096..000000000000 --- a/app/Http/Requests/CreatePaymentTermRequest.php +++ /dev/null @@ -1,30 +0,0 @@ - 'required', - 'name' => 'required', - ]; - } -} diff --git a/app/Http/Requests/UpdatePaymentTermRequest.php b/app/Http/Requests/UpdatePaymentTermRequest.php deleted file mode 100644 index ea9ff80e9772..000000000000 --- a/app/Http/Requests/UpdatePaymentTermRequest.php +++ /dev/null @@ -1,30 +0,0 @@ - 'required|positive', - ]; - - } -} From 9ebb539cce7d81b6334bfbdd833d58f9380be006 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 4 May 2016 14:53:27 +0300 Subject: [PATCH 064/120] API optimizations --- app/Console/Commands/CreateTestData.php | 172 ++++++++++++++++++ app/Console/Kernel.php | 1 + app/Http/Controllers/ExpenseApiController.php | 1 + app/Http/Controllers/InvoiceApiController.php | 2 +- app/Ninja/Transformers/ClientTransformer.php | 2 +- app/Ninja/Transformers/InvoiceTransformer.php | 2 +- app/Ninja/Transformers/VendorTransformer.php | 7 +- database/factories/ModelFactory.php | 48 +++++ 8 files changed, 230 insertions(+), 5 deletions(-) create mode 100644 app/Console/Commands/CreateTestData.php create mode 100644 database/factories/ModelFactory.php diff --git a/app/Console/Commands/CreateTestData.php b/app/Console/Commands/CreateTestData.php new file mode 100644 index 000000000000..4499e1cc09c4 --- /dev/null +++ b/app/Console/Commands/CreateTestData.php @@ -0,0 +1,172 @@ +faker = Factory::create(); + + $this->clientRepo = $clientRepo; + $this->invoiceRepo = $invoiceRepo; + $this->paymentRepo = $paymentRepo; + $this->vendorRepo = $vendorRepo; + $this->expenseRepo = $expenseRepo; + } + + public function fire() + { + if (Utils::isNinjaProd()) { + return false; + } + + $this->info(date('Y-m-d').' Running CreateTestData...'); + + Auth::loginUsingId(1); + $this->count = $this->argument('count'); + + $this->createClients(); + $this->createVendors(); + + $this->info('Done'); + } + + private function createClients() + { + for ($i=0; $i<$this->count; $i++) { + $data = [ + 'name' => $this->faker->name, + 'address1' => $this->faker->streetAddress, + 'address2' => $this->faker->secondaryAddress, + 'city' => $this->faker->city, + 'state' => $this->faker->state, + 'postal_code' => $this->faker->postcode, + 'contacts' => [[ + 'first_name' => $this->faker->firstName, + 'last_name' => $this->faker->lastName, + 'email' => $this->faker->safeEmail, + 'phone' => $this->faker->phoneNumber, + ]] + ]; + + $client = $this->clientRepo->save($data); + $this->info('Client: ' . $client->name); + + $this->createInvoices($client); + } + } + + private function createInvoices($client) + { + for ($i=0; $i<$this->count; $i++) { + $data = [ + 'client_id' => $client->id, + 'invoice_items' => [[ + 'product_key' => $this->faker->word, + 'qty' => $this->faker->randomDigit + 1, + 'cost' => $this->faker->randomFloat(2, 1, 10), + 'notes' => $this->faker->text($this->faker->numberBetween(50, 300)) + ]] + ]; + + $invoice = $this->invoiceRepo->save($data); + $this->info('Invoice: ' . $invoice->invoice_number); + + $this->createPayment($client, $invoice); + } + } + + private function createPayment($client, $invoice) + { + $data = [ + 'invoice_id' => $invoice->id, + 'client_id' => $client->id, + 'amount' => $this->faker->randomFloat(2, 0, $invoice->amount) + ]; + + $payment = $this->paymentRepo->save($data); + + $this->info('Payment: ' . $payment->amount); + } + + private function createVendors() + { + for ($i=0; $i<$this->count; $i++) { + $data = [ + 'name' => $this->faker->name, + 'address1' => $this->faker->streetAddress, + 'address2' => $this->faker->secondaryAddress, + 'city' => $this->faker->city, + 'state' => $this->faker->state, + 'postal_code' => $this->faker->postcode, + 'vendorcontacts' => [[ + 'first_name' => $this->faker->firstName, + 'last_name' => $this->faker->lastName, + 'email' => $this->faker->safeEmail, + 'phone' => $this->faker->phoneNumber, + ]] + ]; + + $vendor = $this->vendorRepo->save($data); + $this->info('Vendor: ' . $vendor->name); + + $this->createExpense($vendor); + } + } + + private function createExpense($vendor) + { + for ($i=0; $i<$this->count; $i++) { + $data = [ + 'vendor_id' => $vendor->id, + 'amount' => $this->faker->randomFloat(2, 1, 10), + 'expense_date' => null, + 'public_notes' => null, + ]; + + $expense = $this->expenseRepo->save($data); + $this->info('Expense: ' . $expense->amount); + } + } + + protected function getArguments() + { + return array( + //array('example', InputArgument::REQUIRED, 'An example argument.'), + ); + } + + protected function getOptions() + { + return array( + //array('example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null), + ); + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 2bc2b1b20787..179e423388f1 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -17,6 +17,7 @@ class Kernel extends ConsoleKernel 'App\Console\Commands\ResetData', 'App\Console\Commands\CheckData', 'App\Console\Commands\PruneData', + 'App\Console\Commands\CreateTestData', 'App\Console\Commands\SendRenewalInvoices', 'App\Console\Commands\ChargeRenewalInvoices', 'App\Console\Commands\SendReminders', diff --git a/app/Http/Controllers/ExpenseApiController.php b/app/Http/Controllers/ExpenseApiController.php index 44722f2a18ce..725067aa1f2c 100644 --- a/app/Http/Controllers/ExpenseApiController.php +++ b/app/Http/Controllers/ExpenseApiController.php @@ -30,6 +30,7 @@ class ExpenseApiController extends BaseAPIController { $expenses = Expense::scope() ->withTrashed() + ->with('client', 'invoice', 'vendor') ->orderBy('created_at','desc'); return $this->listResponse($expenses); diff --git a/app/Http/Controllers/InvoiceApiController.php b/app/Http/Controllers/InvoiceApiController.php index 1361aad9e9a3..667690395eb2 100644 --- a/app/Http/Controllers/InvoiceApiController.php +++ b/app/Http/Controllers/InvoiceApiController.php @@ -60,7 +60,7 @@ class InvoiceApiController extends BaseAPIController { $invoices = Invoice::scope() ->withTrashed() - ->with('invoice_items') + ->with('invoice_items', 'client') ->orderBy('created_at', 'desc'); return $this->listResponse($invoices); diff --git a/app/Ninja/Transformers/ClientTransformer.php b/app/Ninja/Transformers/ClientTransformer.php index 63f0baa0c871..ea282f74ceca 100644 --- a/app/Ninja/Transformers/ClientTransformer.php +++ b/app/Ninja/Transformers/ClientTransformer.php @@ -47,7 +47,7 @@ class ClientTransformer extends EntityTransformer protected $availableIncludes = [ 'invoices', 'credits', - 'expenses', + //'expenses', ]; public function includeContacts(Client $client) diff --git a/app/Ninja/Transformers/InvoiceTransformer.php b/app/Ninja/Transformers/InvoiceTransformer.php index 0933422875c2..c866f1213540 100644 --- a/app/Ninja/Transformers/InvoiceTransformer.php +++ b/app/Ninja/Transformers/InvoiceTransformer.php @@ -28,7 +28,7 @@ class InvoiceTransformer extends EntityTransformer 'invitations', 'payments', 'client', - 'expenses', + //'expenses', ]; public function __construct($account = null, $serializer = null, $client = null) diff --git a/app/Ninja/Transformers/VendorTransformer.php b/app/Ninja/Transformers/VendorTransformer.php index f9c37ac44200..2af79422fde8 100644 --- a/app/Ninja/Transformers/VendorTransformer.php +++ b/app/Ninja/Transformers/VendorTransformer.php @@ -35,10 +35,13 @@ class VendorTransformer extends EntityTransformer * @SWG\Property(property="id_number", type="string", example="123456") */ - protected $availableIncludes = [ + protected $defaultIncludes = [ 'vendorContacts', + ]; + + protected $availableIncludes = [ 'invoices', - 'expenses', + //'expenses', ]; public function includeVendorContacts(Vendor $vendor) diff --git a/database/factories/ModelFactory.php b/database/factories/ModelFactory.php new file mode 100644 index 000000000000..018da7b2357b --- /dev/null +++ b/database/factories/ModelFactory.php @@ -0,0 +1,48 @@ +define(Contact::class, function (Faker\Generator $faker) { + return [ + 'client_id' => function() { + return factory(Client::class)->create()->id; + }, + 'user_id' => 1, + 'account_id' => 1, + 'public_id' => Contact::count() + 1, + 'is_primary' => true, + 'send_invoice' => true, + 'first_name' => $faker->firstName, + 'last_name' => $faker->lastName, + 'email' => $faker->safeEmail, + 'phone' => $faker->phoneNumber, + ]; +}); + +$factory->define(Client::class, function (Faker\Generator $faker) { + return [ + 'user_id' => 1, + 'account_id' => 1, + 'public_id' => Client::count() + 1, + 'name' => $faker->name, + 'address1' => $faker->streetAddress, + 'address2' => $faker->secondaryAddress, + 'city' => $faker->city, + 'state' => $faker->state, + 'postal_code' => $faker->postcode, + 'country_id' => Country::all()->random()->id, + ]; +}); \ No newline at end of file From db9d75ceffb143e71f71c9444b65a928edcb9da9 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 4 May 2016 15:16:49 +0300 Subject: [PATCH 065/120] Changed vendorcontacts to vendor_contacts --- app/Console/Commands/CreateTestData.php | 2 +- app/Http/Controllers/BaseAPIController.php | 2 +- app/Http/Controllers/ExpenseController.php | 6 ++-- app/Http/Controllers/ExportController.php | 4 +-- app/Http/Controllers/VendorApiController.php | 2 +- app/Http/Requests/VendorRequest.php | 4 +-- app/Models/Vendor.php | 4 +-- app/Ninja/Repositories/VendorRepository.php | 6 ++-- app/Ninja/Transformers/VendorTransformer.php | 4 +-- resources/views/vendors/edit.blade.php | 34 ++++++++++---------- resources/views/vendors/show.blade.php | 2 +- tests/acceptance/APICest.php | 2 +- 12 files changed, 36 insertions(+), 36 deletions(-) diff --git a/app/Console/Commands/CreateTestData.php b/app/Console/Commands/CreateTestData.php index 4499e1cc09c4..6a0643e46c5a 100644 --- a/app/Console/Commands/CreateTestData.php +++ b/app/Console/Commands/CreateTestData.php @@ -126,7 +126,7 @@ class CreateTestData extends Command 'city' => $this->faker->city, 'state' => $this->faker->state, 'postal_code' => $this->faker->postcode, - 'vendorcontacts' => [[ + 'vendor_contacts' => [[ 'first_name' => $this->faker->firstName, 'last_name' => $this->faker->lastName, 'email' => $this->faker->safeEmail, diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index 8e48f3bff878..8ffc45ffcf51 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -204,7 +204,7 @@ class BaseAPIController extends Controller } elseif ($include == 'clients') { $data[] = 'clients.contacts'; } elseif ($include == 'vendors') { - $data[] = 'vendors.vendorcontacts'; + $data[] = 'vendors.vendor_contacts'; } elseif ($include) { $data[] = $include; } diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index 3da123c2d972..d4184abf3fca 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -74,7 +74,7 @@ class ExpenseController extends BaseController public function create(ExpenseRequest $request) { if ($request->vendor_id != 0) { - $vendor = Vendor::scope($request->vendor_id)->with('vendorcontacts')->firstOrFail(); + $vendor = Vendor::scope($request->vendor_id)->with('vendor_contacts')->firstOrFail(); } else { $vendor = null; } @@ -85,7 +85,7 @@ class ExpenseController extends BaseController 'method' => 'POST', 'url' => 'expenses', 'title' => trans('texts.new_expense'), - 'vendors' => Vendor::scope()->with('vendorcontacts')->orderBy('name')->get(), + 'vendors' => Vendor::scope()->with('vendor_contacts')->orderBy('name')->get(), 'vendor' => $vendor, 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), 'clientPublicId' => $request->client_id, @@ -124,7 +124,7 @@ class ExpenseController extends BaseController 'url' => 'expenses/'.$expense->public_id, 'title' => 'Edit Expense', 'actions' => $actions, - 'vendors' => Vendor::scope()->with('vendorcontacts')->orderBy('name')->get(), + 'vendors' => Vendor::scope()->with('vendor_contacts')->orderBy('name')->get(), 'vendorPublicId' => $expense->vendor ? $expense->vendor->public_id : null, 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), 'clientPublicId' => $expense->client ? $expense->client->public_id : null, diff --git a/app/Http/Controllers/ExportController.php b/app/Http/Controllers/ExportController.php index db74adb89cee..ebaaa1f6c306 100644 --- a/app/Http/Controllers/ExportController.php +++ b/app/Http/Controllers/ExportController.php @@ -164,12 +164,12 @@ class ExportController extends BaseController if ($request->input(ENTITY_VENDOR)) { $data['clients'] = Vendor::scope() - ->with('user', 'vendorcontacts', 'country') + ->with('user', 'vendor_contacts', 'country') ->withArchived() ->get(); $data['vendor_contacts'] = VendorContact::scope() - ->with('user', 'vendor.contacts') + ->with('user', 'vendor.vendor_contacts') ->withTrashed() ->get(); diff --git a/app/Http/Controllers/VendorApiController.php b/app/Http/Controllers/VendorApiController.php index 2995e5c5e5f2..e15207934cd8 100644 --- a/app/Http/Controllers/VendorApiController.php +++ b/app/Http/Controllers/VendorApiController.php @@ -81,7 +81,7 @@ class VendorApiController extends BaseAPIController $vendor = $this->vendorRepo->save($request->input()); $vendor = Vendor::scope($vendor->public_id) - ->with('country', 'vendorcontacts', 'industry', 'size', 'currency') + ->with('country', 'vendor_contacts', 'industry', 'size', 'currency') ->first(); return $this->itemResponse($vendor); diff --git a/app/Http/Requests/VendorRequest.php b/app/Http/Requests/VendorRequest.php index 600678686c0e..a06e5ad808e6 100644 --- a/app/Http/Requests/VendorRequest.php +++ b/app/Http/Requests/VendorRequest.php @@ -9,8 +9,8 @@ class VendorRequest extends EntityRequest { $vendor = parent::entity(); // eager load the contacts - if ($vendor && ! count($vendor->vendorcontacts)) { - $vendor->load('vendorcontacts'); + if ($vendor && ! count($vendor->vendor_contacts)) { + $vendor->load('vendor_contacts'); } return $vendor; diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php index 573a581e4774..6fcd10f14092 100644 --- a/app/Models/Vendor.php +++ b/app/Models/Vendor.php @@ -95,7 +95,7 @@ class Vendor extends EntityModel return $this->hasMany('App\Models\Payment'); } - public function vendorContacts() + public function vendor_contacts() { return $this->hasMany('App\Models\VendorContact'); } @@ -143,7 +143,7 @@ class Vendor extends EntityModel $contact->fill($data); $contact->is_primary = $isPrimary; - return $this->vendorContacts()->save($contact); + return $this->vendor_contacts()->save($contact); } public function getRoute() diff --git a/app/Ninja/Repositories/VendorRepository.php b/app/Ninja/Repositories/VendorRepository.php index ef5648f47d81..82e1bfdd18f1 100644 --- a/app/Ninja/Repositories/VendorRepository.php +++ b/app/Ninja/Repositories/VendorRepository.php @@ -16,7 +16,7 @@ class VendorRepository extends BaseRepository public function all() { return Vendor::scope() - ->with('user', 'vendorcontacts', 'country') + ->with('user', 'vendor_contacts', 'country') ->withTrashed() ->where('is_deleted', '=', false) ->get(); @@ -71,7 +71,7 @@ class VendorRepository extends BaseRepository } elseif (!$publicId || $publicId == '-1') { $vendor = Vendor::createNew(); } else { - $vendor = Vendor::scope($publicId)->with('vendorcontacts')->firstOrFail(); + $vendor = Vendor::scope($publicId)->with('vendor_contacts')->firstOrFail(); \Log::warning('Entity not set in vendor repo save'); } @@ -79,7 +79,7 @@ class VendorRepository extends BaseRepository $vendor->save(); $first = true; - $vendorcontacts = isset($data['vendorcontact']) ? [$data['vendorcontact']] : $data['vendorcontacts']; + $vendorcontacts = isset($data['vendor_contact']) ? [$data['vendor_contact']] : $data['vendor_contacts']; foreach ($vendorcontacts as $vendorcontact) { $vendorcontact = $vendor->addVendorContact($vendorcontact, $first); diff --git a/app/Ninja/Transformers/VendorTransformer.php b/app/Ninja/Transformers/VendorTransformer.php index 2af79422fde8..df8dc5ea3461 100644 --- a/app/Ninja/Transformers/VendorTransformer.php +++ b/app/Ninja/Transformers/VendorTransformer.php @@ -36,7 +36,7 @@ class VendorTransformer extends EntityTransformer */ protected $defaultIncludes = [ - 'vendorContacts', + 'vendor_contacts', ]; protected $availableIncludes = [ @@ -47,7 +47,7 @@ class VendorTransformer extends EntityTransformer public function includeVendorContacts(Vendor $vendor) { $transformer = new VendorContactTransformer($this->account, $this->serializer); - return $this->includeCollection($vendor->vendorContacts, $transformer, ENTITY_CONTACT); + return $this->includeCollection($vendor->vendor_contacts, $transformer, ENTITY_CONTACT); } public function includeInvoices(Vendor $vendor) diff --git a/resources/views/vendors/edit.blade.php b/resources/views/vendors/edit.blade.php index 5a61287b2ba3..82a619281513 100644 --- a/resources/views/vendors/edit.blade.php +++ b/resources/views/vendors/edit.blade.php @@ -7,8 +7,8 @@ @section('content') -@if ($errors->first('vendorcontacts')) -
    {{ trans($errors->first('vendorcontacts')) }}
    +@if ($errors->first('vendor_contacts')) +
    {{ trans($errors->first('vendor_contacts')) }}
    @endif
    @@ -73,26 +73,26 @@
    -
    {!! Former::hidden('public_id')->data_bind("value: public_id, valueUpdate: 'afterkeydown', - attr: {name: 'vendorcontacts[' + \$index() + '][public_id]'}") !!} + attr: {name: 'vendor_contacts[' + \$index() + '][public_id]'}") !!} {!! Former::text('first_name')->data_bind("value: first_name, valueUpdate: 'afterkeydown', - attr: {name: 'vendorcontacts[' + \$index() + '][first_name]'}") !!} + attr: {name: 'vendor_contacts[' + \$index() + '][first_name]'}") !!} {!! Former::text('last_name')->data_bind("value: last_name, valueUpdate: 'afterkeydown', - attr: {name: 'vendorcontacts[' + \$index() + '][last_name]'}") !!} + attr: {name: 'vendor_contacts[' + \$index() + '][last_name]'}") !!} {!! Former::text('email')->data_bind("value: email, valueUpdate: 'afterkeydown', - attr: {name: 'vendorcontacts[' + \$index() + '][email]', id:'email'+\$index()}") !!} + attr: {name: 'vendor_contacts[' + \$index() + '][email]', id:'email'+\$index()}") !!} {!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown', - attr: {name: 'vendorcontacts[' + \$index() + '][phone]'}") !!} + attr: {name: 'vendor_contacts[' + \$index() + '][phone]'}") !!}
    - + {!! link_to('#', trans('texts.remove_contact').' -', array('data-bind'=>'click: $parent.removeContact')) !!} - + {!! link_to('#', trans('texts.add_contact').' +', array('onclick'=>'return addContact()')) !!}
    @@ -186,10 +186,10 @@ function VendorModel(data) { var self = this; - self.vendorcontacts = ko.observableArray(); + self.vendor_contacts = ko.observableArray(); self.mapping = { - 'vendorcontacts': { + 'vendor_contacts': { create: function(options) { return new VendorContactModel(options.data); } @@ -199,12 +199,12 @@ if (data) { ko.mapping.fromJS(data, self.mapping, this); } else { - self.vendorcontacts.push(new VendorContactModel()); + self.vendor_contacts.push(new VendorContactModel()); } self.placeholderName = ko.computed(function() { - if (self.vendorcontacts().length == 0) return ''; - var contact = self.vendorcontacts()[0]; + if (self.vendor_contacts().length == 0) return ''; + var contact = self.vendor_contacts()[0]; if (contact.first_name() || contact.last_name()) { return contact.first_name() + ' ' + contact.last_name(); } else { @@ -226,12 +226,12 @@ ko.applyBindings(model); function addContact() { - model.vendorcontacts.push(new VendorContactModel()); + model.vendor_contacts.push(new VendorContactModel()); return false; } model.removeContact = function() { - model.vendorcontacts.remove(this); + model.vendor_contacts.remove(this); } diff --git a/resources/views/vendors/show.blade.php b/resources/views/vendors/show.blade.php index 62037179e741..37572ee4c5bc 100644 --- a/resources/views/vendors/show.blade.php +++ b/resources/views/vendors/show.blade.php @@ -109,7 +109,7 @@

    {{ trans('texts.contacts') }}

    - @foreach ($vendor->vendorcontacts as $contact) + @foreach ($vendor->vendor_contacts as $contact) @if ($contact->first_name || $contact->last_name) {{ $contact->first_name.' '.$contact->last_name }}
    @endif diff --git a/tests/acceptance/APICest.php b/tests/acceptance/APICest.php index ee36c6265e5c..41e922539677 100644 --- a/tests/acceptance/APICest.php +++ b/tests/acceptance/APICest.php @@ -78,7 +78,7 @@ class APICest $data = new stdClass; $data->name = $this->faker->word; - $data->vendorcontacts = []; + $data->vendor_contacts = []; $this->createEntity('vendor', $data); $this->listEntities('vendor'); From 3300619d28bfd29f0011f69baa8768c673e634d6 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 4 May 2016 23:27:12 +0300 Subject: [PATCH 066/120] Added support for gzip to the .htaccess file --- .htaccess | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/.htaccess b/.htaccess index 27a6945d38f8..8b1f582ca455 100644 --- a/.htaccess +++ b/.htaccess @@ -6,3 +6,141 @@ # https://coderwall.com/p/erbaig/laravel-s-htaccess-to-remove-public-from-url # RewriteRule ^(.*)$ public/$1 [L] + +# https://github.com/h5bp/server-configs-apache/blob/master/dist/.htaccess + + +# ###################################################################### +# # INTERNET EXPLORER # +# ###################################################################### + +# ---------------------------------------------------------------------- +# | Iframes cookies | +# ---------------------------------------------------------------------- + +# Allow cookies to be set from iframes in Internet Explorer. +# +# https://msdn.microsoft.com/en-us/library/ms537343.aspx +# http://www.w3.org/TR/2000/CR-P3P-20001215/ + + + Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"" + + + +# ###################################################################### +# # MEDIA TYPES AND CHARACTER ENCODINGS # +# ###################################################################### + +# ---------------------------------------------------------------------- +# | Character encodings | +# ---------------------------------------------------------------------- + +# Serve all resources labeled as `text/html` or `text/plain` +# with the media type `charset` parameter set to `UTF-8`. +# +# https://httpd.apache.org/docs/current/mod/core.html#adddefaultcharset + +AddDefaultCharset utf-8 + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Serve the following file types with the media type `charset` +# parameter set to `UTF-8`. +# +# https://httpd.apache.org/docs/current/mod/mod_mime.html#addcharset + + + AddCharset utf-8 .atom \ + .bbaw \ + .css \ + .geojson \ + .js \ + .json \ + .jsonld \ + .manifest \ + .rdf \ + .rss \ + .topojson \ + .vtt \ + .webapp \ + .webmanifest \ + .xloc \ + .xml + + + +# ###################################################################### +# # WEB PERFORMANCE # +# ###################################################################### + +# ---------------------------------------------------------------------- +# | Compression | +# ---------------------------------------------------------------------- + + + + # Force compression for mangled headers. + # https://developer.yahoo.com/blogs/ydn/pushing-beyond-gzipping-25601.html + + + + SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding + RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding + + + + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + # Map certain file types to the specified encoding type in order to + # make Apache serve them with the appropriate `Content-Encoding` HTTP + # response header (this will NOT make Apache compress them!). + + # If the following file types wouldn't be served without the appropriate + # `Content-Enable` HTTP response header, client applications (e.g.: + # browsers) wouldn't know that they first need to uncompress the response, + # and thus, wouldn't be able to understand the content. + + # http://httpd.apache.org/docs/current/mod/mod_mime.html#addencoding + + + AddEncoding gzip svgz + + + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + # Compress all output labeled with one of the following media types. + + # IMPORTANT: For Apache versions below 2.3.7 you don't need to enable + # `mod_filter` and can remove the `` & `` + # lines as `AddOutputFilterByType` is still in the core directives. + + + AddOutputFilterByType DEFLATE "application/atom+xml" \ + "application/javascript" \ + "application/json" \ + "application/ld+json" \ + "application/manifest+json" \ + "application/rdf+xml" \ + "application/rss+xml" \ + "application/schema+json" \ + "application/vnd.geo+json" \ + "application/vnd.ms-fontobject" \ + "application/x-font-ttf" \ + "application/x-web-app-manifest+json" \ + "application/xhtml+xml" \ + "application/xml" \ + "font/opentype" \ + "image/svg+xml" \ + "image/x-icon" \ + "text/cache-manifest" \ + "text/css" \ + "text/html" \ + "text/javascript" \ + "text/plain" \ + "text/vtt" \ + "text/x-component" \ + "text/xml" + + + \ No newline at end of file From 32bf47aacff801a43aa188a1aba2e228e4ee2a9b Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Wed, 4 May 2016 23:49:20 +0300 Subject: [PATCH 067/120] Added support for filtering API lists by updated_at --- app/Http/Controllers/BaseAPIController.php | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index 8ffc45ffcf51..a172c2e1c5db 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -90,10 +90,21 @@ class BaseAPIController extends Controller $transformerClass = EntityModel::getTransformerName($this->entityType); $transformer = new $transformerClass(Auth::user()->account, Input::get('serializer')); - $include = $transformer->getDefaultIncludes(); - $include = $this->getRequestIncludes($include); - $query->with($include); - + $includes = $transformer->getDefaultIncludes(); + $includes = $this->getRequestIncludes($includes); + + if ($updatedAt = Input::get('updated_at')) { + $query->where('updated_at', '>=', $updatedAt); + } + + foreach ($includes as $include) { + $query->with([$include => function($query) use ($updatedAt) { + if ($updatedAt) { + $query->where('updated_at', '>=', $updatedAt); + } + }]); + } + if ($clientPublicId = Input::get('client_id')) { $filter = function($query) use ($clientPublicId) { $query->where('public_id', '=', $clientPublicId); @@ -156,7 +167,7 @@ class BaseAPIController extends Controller if (Utils::isNinjaDev()) { $count = count(\DB::getQueryLog()); Log::info(Request::method() . ' - ' . Request::url() . ": $count queries"); - //Log::info(print_r(\DB::getQueryLog(), true)); + //Log::info(json_encode(\DB::getQueryLog())); } $index = Request::get('index') ?: 'data'; From 5d00d186012c3e4494d89eef7d637fe6b6e64d12 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 00:26:39 +0300 Subject: [PATCH 068/120] Added support for filtering API lists by updated_at --- app/Http/Controllers/BaseAPIController.php | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/app/Http/Controllers/BaseAPIController.php b/app/Http/Controllers/BaseAPIController.php index a172c2e1c5db..0718e95b91e4 100644 --- a/app/Http/Controllers/BaseAPIController.php +++ b/app/Http/Controllers/BaseAPIController.php @@ -93,17 +93,19 @@ class BaseAPIController extends Controller $includes = $transformer->getDefaultIncludes(); $includes = $this->getRequestIncludes($includes); - if ($updatedAt = Input::get('updated_at')) { - $query->where('updated_at', '>=', $updatedAt); - } + $query->with($includes); - foreach ($includes as $include) { - $query->with([$include => function($query) use ($updatedAt) { - if ($updatedAt) { - $query->where('updated_at', '>=', $updatedAt); + if ($updatedAt = Input::get('updated_at')) { + $updatedAt = date('Y-m-d H:i:s', $updatedAt); + $query->where(function($query) use ($includes, $updatedAt) { + $query->where('updated_at', '>=', $updatedAt); + foreach ($includes as $include) { + $query->orWhereHas($include, function($query) use ($updatedAt) { + $query->where('updated_at', '>=', $updatedAt); + }); } - }]); - } + }); + } if ($clientPublicId = Input::get('client_id')) { $filter = function($query) use ($clientPublicId) { @@ -167,7 +169,7 @@ class BaseAPIController extends Controller if (Utils::isNinjaDev()) { $count = count(\DB::getQueryLog()); Log::info(Request::method() . ' - ' . Request::url() . ": $count queries"); - //Log::info(json_encode(\DB::getQueryLog())); + Log::info(json_encode(\DB::getQueryLog())); } $index = Request::get('index') ?: 'data'; From 3b508f656b5f0f2d26be0588790e71172b2f0958 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 09:35:39 +0300 Subject: [PATCH 069/120] Fix for travis test --- tests/acceptance/TaxRatesCest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/acceptance/TaxRatesCest.php b/tests/acceptance/TaxRatesCest.php index ebe68f7b5880..e4db10b15909 100644 --- a/tests/acceptance/TaxRatesCest.php +++ b/tests/acceptance/TaxRatesCest.php @@ -84,7 +84,8 @@ class TaxRatesCest // check total is right after saving $I->see("\${$total}"); $I->amOnPage('/invoices'); - + $I->wait(2); + // check total is right in list view $I->see("\${$total}"); } From ef2bc3d811be4140198b91baacdc0440f83ecaa4 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 09:52:59 +0300 Subject: [PATCH 070/120] White label changes --- resources/lang/en/texts.php | 2 +- resources/views/accounts/management.blade.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index f101c79564e7..25eaa81de8e2 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -992,7 +992,7 @@ $LANG = array( 'overdue' => 'Overdue', - 'white_label_text' => 'Purchase a ONE YEAR white label license for $'.WHITE_LABEL_PRICE.' to remove the Invoice Ninja branding from the client portal and help support our project.', + 'white_label_text' => 'Purchase a ONE YEAR white label license for $'.WHITE_LABEL_PRICE.' to remove the Invoice Ninja branding from the client portal and emails.', 'user_email_footer' => 'To adjust your email notification settings please visit '.SITE_URL.'/settings/notifications', 'reset_password_footer' => 'If you did not request this password reset please email our support: '.CONTACT_EMAIL, 'limit_users' => 'Sorry, this will exceed the limit of '.MAX_NUM_USERS.' users', diff --git a/resources/views/accounts/management.blade.php b/resources/views/accounts/management.blade.php index cf9917deb005..cfb7b6eb28ee 100644 --- a/resources/views/accounts/management.blade.php +++ b/resources/views/accounts/management.blade.php @@ -94,9 +94,9 @@
    @endif @if (Utils::isNinjaProd()) - {!! Former::actions( Button::success(trans('texts.plan_upgrade'))->large()->withAttributes(['onclick' => 'showChangePlan()'])->appendIcon(Icon::create('plus-sign'))) !!} - @else - {!! Former::actions( Button::success(trans('texts.white_label_button'))->large()->withAttributes(['onclick' => 'loadImages("#whiteLabelModal");$("#whiteLabelModal").modal("show");'])->appendIcon(Icon::create('plus-sign'))) !!} + {!! Former::actions( Button::success(trans('texts.plan_upgrade'))->large()->withAttributes(['onclick' => 'showChangePlan()'])->appendIcon(Icon::create('plus-sign'))) !!} + @elseif (!$account->hasFeature(FEATURE_WHITE_LABEL)) + {!! Former::actions( Button::success(trans('texts.white_label_button'))->large()->withAttributes(['onclick' => 'loadImages("#whiteLabelModal");$("#whiteLabelModal").modal("show");'])->appendIcon(Icon::create('plus-sign'))) !!} @endif @endif
    From 7744c6c41ccbdadf1b40758576cdb53db5ad4f36 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 09:58:14 +0300 Subject: [PATCH 071/120] White label changes --- resources/lang/en/texts.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 25eaa81de8e2..f101c79564e7 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -992,7 +992,7 @@ $LANG = array( 'overdue' => 'Overdue', - 'white_label_text' => 'Purchase a ONE YEAR white label license for $'.WHITE_LABEL_PRICE.' to remove the Invoice Ninja branding from the client portal and emails.', + 'white_label_text' => 'Purchase a ONE YEAR white label license for $'.WHITE_LABEL_PRICE.' to remove the Invoice Ninja branding from the client portal and help support our project.', 'user_email_footer' => 'To adjust your email notification settings please visit '.SITE_URL.'/settings/notifications', 'reset_password_footer' => 'If you did not request this password reset please email our support: '.CONTACT_EMAIL, 'limit_users' => 'Sorry, this will exceed the limit of '.MAX_NUM_USERS.' users', From 2a5de605dcddd4c944d8aee124647e7151307dca Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 10:15:51 +0300 Subject: [PATCH 072/120] Added relationLoaded checks to optimize queries --- app/Http/Requests/ClientRequest.php | 2 +- app/Http/Requests/ExpenseRequest.php | 6 +++--- app/Http/Requests/InvoiceRequest.php | 4 ++-- app/Http/Requests/VendorRequest.php | 2 +- app/Models/Account.php | 6 ++++-- app/Models/Client.php | 10 +++++++--- 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/app/Http/Requests/ClientRequest.php b/app/Http/Requests/ClientRequest.php index adbe2d4c2ae6..ec28cdb77d1d 100644 --- a/app/Http/Requests/ClientRequest.php +++ b/app/Http/Requests/ClientRequest.php @@ -9,7 +9,7 @@ class ClientRequest extends EntityRequest { $client = parent::entity(); // eager load the contacts - if ($client && ! count($client->contacts)) { + if ($client && ! $client->relationLoaded('contacts')) { $client->load('contacts'); } diff --git a/app/Http/Requests/ExpenseRequest.php b/app/Http/Requests/ExpenseRequest.php index d5e2c793c371..ae2e83b6d12f 100644 --- a/app/Http/Requests/ExpenseRequest.php +++ b/app/Http/Requests/ExpenseRequest.php @@ -8,11 +8,11 @@ class ExpenseRequest extends EntityRequest { { $expense = parent::entity(); - // eager load the contacts - if ($expense && ! count($expense->documents)) { + // eager load the documents + if ($expense && ! $expense->relationLoaded('documents')) { $expense->load('documents'); } - + return $expense; } } \ No newline at end of file diff --git a/app/Http/Requests/InvoiceRequest.php b/app/Http/Requests/InvoiceRequest.php index bf24c38839a9..5e2d93139003 100644 --- a/app/Http/Requests/InvoiceRequest.php +++ b/app/Http/Requests/InvoiceRequest.php @@ -8,8 +8,8 @@ class InvoiceRequest extends EntityRequest { { $invoice = parent::entity(); - // eager load the contacts - if ($invoice && ! count($invoice->invoice_items)) { + // eager load the invoice items + if ($invoice && ! $invoice->relationLoaded('invoice_items')) { $invoice->load('invoice_items'); } diff --git a/app/Http/Requests/VendorRequest.php b/app/Http/Requests/VendorRequest.php index a06e5ad808e6..8f96e7c55025 100644 --- a/app/Http/Requests/VendorRequest.php +++ b/app/Http/Requests/VendorRequest.php @@ -9,7 +9,7 @@ class VendorRequest extends EntityRequest { $vendor = parent::entity(); // eager load the contacts - if ($vendor && ! count($vendor->vendor_contacts)) { + if ($vendor && ! $vendor->relationLoaded('vendor_contacts')) { $vendor->load('vendor_contacts'); } diff --git a/app/Models/Account.php b/app/Models/Account.php index c8ece3b4da95..5814ec76423a 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -212,7 +212,9 @@ class Account extends Eloquent public function isGatewayConfigured($gatewayId = 0) { - $this->load('account_gateways'); + if ( ! $this->relationLoaded('account_gateways')) { + $this->load('account_gateways'); + } if ($gatewayId) { return $this->getGatewayConfig($gatewayId) != false; @@ -241,7 +243,7 @@ class Account extends Eloquent return $this->name; } - $this->load('users'); + //$this->load('users'); $user = $this->users()->first(); return $user->getDisplayName(); diff --git a/app/Models/Client.php b/app/Models/Client.php index 85cb543a6b03..4b26f40df0a7 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -256,13 +256,17 @@ class Client extends EntityModel public function getGatewayToken() { - $this->account->load('account_gateways'); + $account = $this->account; + + if ( ! $account->relationLoaded('account_gateways')) { + $account->load('account_gateways'); + } - if (!count($this->account->account_gateways)) { + if (!count($account->account_gateways)) { return false; } - $accountGateway = $this->account->getGatewayConfig(GATEWAY_STRIPE); + $accountGateway = $account->getGatewayConfig(GATEWAY_STRIPE); if (!$accountGateway) { return false; From a00a86a57898b52b4679c2b15b82c9e448c8f2fd Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 10:23:09 +0300 Subject: [PATCH 073/120] Fix for travis --- tests/acceptance/OnlinePaymentCest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/acceptance/OnlinePaymentCest.php b/tests/acceptance/OnlinePaymentCest.php index 44bb04ea6498..7e85f2cc6b5a 100644 --- a/tests/acceptance/OnlinePaymentCest.php +++ b/tests/acceptance/OnlinePaymentCest.php @@ -96,7 +96,7 @@ class OnlinePaymentCest $I->click('table.invoice-table tbody tr:nth-child(1) .tt-selectable'); $I->checkOption('#auto_bill'); $I->executeJS('preparePdfData(\'email\')'); - $I->wait(2); + $I->wait(3); $I->see("$0.00"); } From 9f8865d0f22d55c59a1d6606e6148ab543f4ad33 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 10:28:23 +0300 Subject: [PATCH 074/120] Added pages to navigation search --- app/Ninja/Repositories/AccountRepository.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/Ninja/Repositories/AccountRepository.php b/app/Ninja/Repositories/AccountRepository.php index e8fe2d041843..e650b28a419e 100644 --- a/app/Ninja/Repositories/AccountRepository.php +++ b/app/Ninja/Repositories/AccountRepository.php @@ -167,6 +167,7 @@ class AccountRepository ENTITY_QUOTE, ENTITY_TASK, ENTITY_EXPENSE, + ENTITY_VENDOR, ENTITY_RECURRING_INVOICE, ENTITY_PAYMENT, ENTITY_CREDIT @@ -192,6 +193,10 @@ class AccountRepository $settings = array_merge(Account::$basicSettings, Account::$advancedSettings); + if ( ! Utils::isNinjaProd()) { + $settings[] = ACCOUNT_SYSTEM_SETTINGS; + } + foreach ($settings as $setting) { $features[] = [ $setting, From 501958cc1ec013cad8e11183d38880f8b577016c Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 11:55:09 +0300 Subject: [PATCH 075/120] Added language seeder --- app/Http/Controllers/AppController.php | 13 +------- database/seeds/DatabaseSeeder.php | 1 + database/seeds/LanguageSeeder.php | 41 ++++++++++++++++++++++++++ database/seeds/UpdateSeeder.php | 1 + 4 files changed, 44 insertions(+), 12 deletions(-) create mode 100644 database/seeds/LanguageSeeder.php diff --git a/app/Http/Controllers/AppController.php b/app/Http/Controllers/AppController.php index 568cb4d45f5b..a602ae7ecb85 100644 --- a/app/Http/Controllers/AppController.php +++ b/app/Http/Controllers/AppController.php @@ -266,18 +266,7 @@ class AppController extends BaseController Cache::flush(); Session::flush(); Artisan::call('migrate', array('--force' => true)); - foreach ([ - 'PaymentLibraries', - 'Fonts', - 'Banks', - 'InvoiceStatus', - 'Currencies', - 'DateFormats', - 'InvoiceDesigns', - 'PaymentTerms', - ] as $seeder) { - Artisan::call('db:seed', array('--force' => true, '--class' => "{$seeder}Seeder")); - } + Artisan::call('db:seed', array('--force' => true, '--class' => "UpdateSeeder")); Event::fire(new UserSettingsChanged()); Session::flash('message', trans('texts.processed_updates')); } catch (Exception $e) { diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php index 8791f30e71fb..d7f38d3e1e53 100644 --- a/database/seeds/DatabaseSeeder.php +++ b/database/seeds/DatabaseSeeder.php @@ -23,5 +23,6 @@ class DatabaseSeeder extends Seeder $this->call('DateFormatsSeeder'); $this->call('InvoiceDesignsSeeder'); $this->call('PaymentTermsSeeder'); + $this->call('LanguageSeeder'); } } diff --git a/database/seeds/LanguageSeeder.php b/database/seeds/LanguageSeeder.php new file mode 100644 index 000000000000..c82b2630771c --- /dev/null +++ b/database/seeds/LanguageSeeder.php @@ -0,0 +1,41 @@ + 'English', 'locale' => 'en'], + ['name' => 'Italian', 'locale' => 'it'], + ['name' => 'German', 'locale' => 'de'], + ['name' => 'French', 'locale' => 'fr'], + ['name' => 'Brazilian Portuguese', 'locale' => 'pt_BR'], + ['name' => 'Dutch', 'locale' => 'nl'], + ['name' => 'Spanish', 'locale' => 'es'], + ['name' => 'Norwegian', 'locale' => 'nb_NO'], + ['name' => 'Danish', 'locale' => 'da'], + ['name' => 'Japanese', 'locale' => 'ja'], + ['name' => 'Swedish', 'locale' => 'sv'], + ['name' => 'Spanish - Spain', 'locale' => 'es_ES'], + ['name' => 'French - Canada', 'locale' => 'fr_CA'], + ['name' => 'Lithuanian', 'locale' => 'lt'], + ['name' => 'Polish', 'locale' => 'pl'], + ]; + + foreach ($languages as $language) { + $record = Language::whereLocale($language['locale'])->first(); + if ($record) { + $record->name = $language['name']; + $record->save(); + } else { + Language::create($language); + } + } + + Eloquent::reguard(); + } +} diff --git a/database/seeds/UpdateSeeder.php b/database/seeds/UpdateSeeder.php index e4d43f78e512..d445811de506 100644 --- a/database/seeds/UpdateSeeder.php +++ b/database/seeds/UpdateSeeder.php @@ -19,5 +19,6 @@ class UpdateSeeder extends Seeder $this->call('DateFormatsSeeder'); $this->call('InvoiceDesignsSeeder'); $this->call('PaymentTermsSeeder'); + $this->call('LanguageSeeder'); } } From c79d9fe24cf3fc776a69ceb1479111bf1d4e00fc Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 5 May 2016 17:46:22 +0300 Subject: [PATCH 076/120] Added preview for email templates --- app/Http/Controllers/AccountController.php | 25 +++++ app/Http/routes.php | 1 + app/Models/Account.php | 5 + app/Ninja/Mailers/ContactMailer.php | 103 ++---------------- app/Services/TemplateService.php | 80 ++++++++++++++ resources/lang/en/texts.php | 1 + resources/views/accounts/template.blade.php | 7 +- .../templates_and_reminders.blade.php | 36 ++++++ resources/views/emails/design2_html.blade.php | 22 ++-- ..._html.blade.php => design3_html.blade.php} | 22 ++-- ..._text.blade.php => design3_text.blade.php} | 0 .../emails/partials/account_logo.blade.php | 2 +- 12 files changed, 188 insertions(+), 116 deletions(-) create mode 100644 app/Services/TemplateService.php rename resources/views/emails/{design1_html.blade.php => design3_html.blade.php} (72%) rename resources/views/emails/{design1_text.blade.php => design3_text.blade.php} (100%) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 3f8917b65427..a55188338073 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -1301,4 +1301,29 @@ class AccountController extends BaseController return Redirect::to("/settings/$section/", 301); } + + public function previewEmail(\App\Services\TemplateService $templateService) + { + $template = Input::get('template'); + $invoice = Invoice::scope()->first(); + $account = Auth::user()->account; + + // replace the variables with sample data + $data = [ + 'account' => $account, + 'invoice' => $invoice, + 'invitation' => $invoice->invitations->first(), + 'client' => $invoice->client, + 'amount' => $invoice->amount + ]; + + // create the email view + $view = 'emails.' . $account->getTemplateView() . '_html'; + $data = array_merge($data, [ + 'body' => $templateService->processVariables($template, $data), + 'entityType' => ENTITY_INVOICE, + ]); + + return Response::view($view, $data); + } } diff --git a/app/Http/routes.php b/app/Http/routes.php index df1f1add4bb8..c1162db275c1 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -208,6 +208,7 @@ Route::group([ Route::resource('tax_rates', 'TaxRateController'); Route::post('tax_rates/bulk', 'TaxRateController@bulk'); + Route::get('settings/email_preview', 'AccountController@previewEmail'); Route::get('company/{section}/{subSection?}', 'AccountController@redirectLegacy'); Route::get('settings/data_visualizations', 'ReportController@d3'); Route::get('settings/charts_and_reports', 'ReportController@showReports'); diff --git a/app/Models/Account.php b/app/Models/Account.php index 5814ec76423a..92e2a623ff7b 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -1169,6 +1169,11 @@ class Account extends Eloquent return str_replace('/>', ' />', $template); } + public function getTemplateView($view = '') + { + return $this->getEmailDesignId() == EMAIL_DESIGN_PLAIN ? $view : 'design' . $this->getEmailDesignId(); + } + public function getEmailFooter() { if ($this->email_footer) { diff --git a/app/Ninja/Mailers/ContactMailer.php b/app/Ninja/Mailers/ContactMailer.php index 4b176628ebf0..fbebd0c5fdfd 100644 --- a/app/Ninja/Mailers/ContactMailer.php +++ b/app/Ninja/Mailers/ContactMailer.php @@ -1,17 +1,13 @@ templateService = $templateService; + } + public function sendInvoice(Invoice $invoice, $reminder = false, $pdfString = false) { $invoice->load('invitations', 'client.language', 'account'); @@ -144,7 +145,7 @@ class ContactMailer extends Mailer } $data = [ - 'body' => $this->processVariables($body, $variables), + 'body' => $this->templateService->processVariables($body, $variables), 'link' => $invitation->getLink(), 'entityType' => $invoice->getEntityType(), 'invoiceId' => $invoice->id, @@ -160,14 +161,9 @@ class ContactMailer extends Mailer $data['pdfFileName'] = $invoice->getFileName(); } - $subject = $this->processVariables($subject, $variables); + $subject = $this->templateService->processVariables($subject, $variables); $fromEmail = $user->email; - - if ($account->getEmailDesignId() == EMAIL_DESIGN_PLAIN) { - $view = ENTITY_INVOICE; - } else { - $view = 'design' . ($account->getEmailDesignId() - 1); - } + $view = $account->getTemplateView(ENTITY_INVOICE); $response = $this->sendTo($invitation->contact->email, $fromEmail, $account->getDisplayName(), $subject, $view, $data); @@ -230,7 +226,7 @@ class ContactMailer extends Mailer ]; $data = [ - 'body' => $this->processVariables($emailTemplate, $variables), + 'body' => $this->templateService->processVariables($emailTemplate, $variables), 'link' => $invitation->getLink(), 'invoice' => $invoice, 'client' => $client, @@ -244,14 +240,10 @@ class ContactMailer extends Mailer $data['pdfFileName'] = $invoice->getFileName(); } - $subject = $this->processVariables($emailSubject, $variables); + $subject = $this->templateService->processVariables($emailSubject, $variables); $data['invoice_id'] = $payment->invoice->id; - if ($account->getEmailDesignId() == EMAIL_DESIGN_PLAIN) { - $view = 'payment_confirmation'; - } else { - $view = 'design' . ($account->getEmailDesignId() - 1); - } + $view = $account->getTemplateView('payment_confirmation'); if ($user->email && $contact->email) { $this->sendTo($contact->email, $user->email, $accountName, $subject, $view, $data); @@ -281,75 +273,4 @@ class ContactMailer extends Mailer $this->sendTo($email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data); } - - private function processVariables($template, $data) - { - $account = $data['account']; - $client = $data['client']; - $invitation = $data['invitation']; - $invoice = $invitation->invoice; - $passwordHTML = isset($data['password'])?'

    '.trans('texts.password').': '.$data['password'].'

    ':false; - $documentsHTML = ''; - - if($account->hasFeature(FEATURE_DOCUMENTS) && $invoice->hasDocuments()){ - $documentsHTML .= trans('texts.email_documents_header').'

    '; - } - - $variables = [ - '$footer' => $account->getEmailFooter(), - '$client' => $client->getDisplayName(), - '$account' => $account->getDisplayName(), - '$dueDate' => $account->formatDate($invoice->due_date), - '$invoiceDate' => $account->formatDate($invoice->invoice_date), - '$contact' => $invitation->contact->getDisplayName(), - '$firstName' => $invitation->contact->first_name, - '$amount' => $account->formatMoney($data['amount'], $client), - '$invoice' => $invoice->invoice_number, - '$quote' => $invoice->invoice_number, - '$link' => $invitation->getLink(), - '$password' => $passwordHTML, - '$viewLink' => $invitation->getLink().'$password', - '$viewButton' => Form::emailViewButton($invitation->getLink(), $invoice->getEntityType()).'$password', - '$paymentLink' => $invitation->getLink('payment').'$password', - '$paymentButton' => Form::emailPaymentButton($invitation->getLink('payment')).'$password', - '$customClient1' => $account->custom_client_label1, - '$customClient2' => $account->custom_client_label2, - '$customInvoice1' => $account->custom_invoice_text_label1, - '$customInvoice2' => $account->custom_invoice_text_label2, - '$documents' => $documentsHTML, - ]; - - // Add variables for available payment types - foreach (Gateway::$paymentTypes as $type) { - $camelType = Gateway::getPaymentTypeName($type); - $type = Utils::toSnakeCase($camelType); - $variables["\${$camelType}Link"] = $invitation->getLink('payment') . "/{$type}"; - $variables["\${$camelType}Button"] = Form::emailPaymentButton($invitation->getLink('payment') . "/{$type}"); - } - - $includesPasswordPlaceholder = strpos($template, '$password') !== false; - - $str = str_replace(array_keys($variables), array_values($variables), $template); - - if(!$includesPasswordPlaceholder && $passwordHTML){ - $pos = strrpos($str, '$password'); - if($pos !== false) - { - $str = substr_replace($str, $passwordHTML, $pos, 9/* length of "$password" */); - } - } - $str = str_replace('$password', '', $str); - $str = autolink($str, 100); - - return $str; - } } diff --git a/app/Services/TemplateService.php b/app/Services/TemplateService.php new file mode 100644 index 000000000000..5a41c705352d --- /dev/null +++ b/app/Services/TemplateService.php @@ -0,0 +1,80 @@ +invoice; + $passwordHTML = isset($data['password'])?'

    '.trans('texts.password').': '.$data['password'].'

    ':false; + $documentsHTML = ''; + + if ($account->hasFeature(FEATURE_DOCUMENTS) && $invoice->hasDocuments()) { + $documentsHTML .= trans('texts.email_documents_header').'

    '; + } + + $variables = [ + '$footer' => $account->getEmailFooter(), + '$client' => $client->getDisplayName(), + '$account' => $account->getDisplayName(), + '$dueDate' => $account->formatDate($invoice->due_date), + '$invoiceDate' => $account->formatDate($invoice->invoice_date), + '$contact' => $invitation->contact->getDisplayName(), + '$firstName' => $invitation->contact->first_name, + '$amount' => $account->formatMoney($data['amount'], $client), + '$invoice' => $invoice->invoice_number, + '$quote' => $invoice->invoice_number, + '$link' => $invitation->getLink(), + '$password' => $passwordHTML, + '$viewLink' => $invitation->getLink().'$password', + '$viewButton' => Form::emailViewButton($invitation->getLink(), $invoice->getEntityType()).'$password', + '$paymentLink' => $invitation->getLink('payment').'$password', + '$paymentButton' => Form::emailPaymentButton($invitation->getLink('payment')).'$password', + '$customClient1' => $account->custom_client_label1, + '$customClient2' => $account->custom_client_label2, + '$customInvoice1' => $account->custom_invoice_text_label1, + '$customInvoice2' => $account->custom_invoice_text_label2, + '$documents' => $documentsHTML, + ]; + + // Add variables for available payment types + foreach (Gateway::$paymentTypes as $type) { + $camelType = Gateway::getPaymentTypeName($type); + $type = Utils::toSnakeCase($camelType); + $variables["\${$camelType}Link"] = $invitation->getLink('payment') . "/{$type}"; + $variables["\${$camelType}Button"] = Form::emailPaymentButton($invitation->getLink('payment') . "/{$type}"); + } + + $includesPasswordPlaceholder = strpos($template, '$password') !== false; + + $str = str_replace(array_keys($variables), array_values($variables), $template); + + if (!$includesPasswordPlaceholder && $passwordHTML) { + $pos = strrpos($str, '$password'); + if ($pos !== false) + { + $str = substr_replace($str, $passwordHTML, $pos, 9/* length of "$password" */); + } + } + $str = str_replace('$password', '', $str); + $str = autolink($str, 100); + + return $str; + } +} \ No newline at end of file diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index f101c79564e7..6d6f3229b059 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -1176,6 +1176,7 @@ $LANG = array( 'page_size' => 'Page Size', 'live_preview_disabled' => 'Live preview has been disabled to support selected font', 'invoice_number_padding' => 'Padding', + 'preview' => 'Preview', ); diff --git a/resources/views/accounts/template.blade.php b/resources/views/accounts/template.blade.php index 0d4294af5b6f..68279299f607 100644 --- a/resources/views/accounts/template.blade.php +++ b/resources/views/accounts/template.blade.php @@ -62,11 +62,14 @@
    +

     

    -
    -

     

    +

    @include('partials/quill_toolbar', ['name' => $field])
    +
    + {!! Button::primary(trans('texts.preview'))->withAttributes(['onclick' => 'serverPreview("'.$field.'")'])->small() !!} +
    diff --git a/resources/views/accounts/templates_and_reminders.blade.php b/resources/views/accounts/templates_and_reminders.blade.php index 067433a21763..d8032f352a92 100644 --- a/resources/views/accounts/templates_and_reminders.blade.php +++ b/resources/views/accounts/templates_and_reminders.blade.php @@ -80,6 +80,26 @@
    + + +